dirmngr: Put brackets around IP addresses in the hosttable.
[gnupg.git] / dirmngr / ldap.c
1 /* ldap.c - LDAP access
2  * Copyright (C) 2002 Klarälvdalens Datakonsult AB
3  * Copyright (C) 2003, 2004, 2005, 2007, 2008, 2010 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, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
20  */
21
22 #include <config.h>
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <errno.h>
28 #include <unistd.h>
29 #include <fcntl.h>
30 #include <time.h>
31 #include <npth.h>
32
33 #include "dirmngr.h"
34 #include "exechelp.h"
35 #include "crlfetch.h"
36 #include "ldapserver.h"
37 #include "misc.h"
38 #include "ldap-wrapper.h"
39
40
41 #define UNENCODED_URL_CHARS "abcdefghijklmnopqrstuvwxyz"   \
42                             "ABCDEFGHIJKLMNOPQRSTUVWXYZ"   \
43                             "01234567890"                  \
44                             "$-_.+!*'(),"
45 #define USERCERTIFICATE "userCertificate"
46 #define CACERTIFICATE   "caCertificate"
47 #define X509CACERT      "x509caCert"
48 #define USERSMIMECERTIFICATE "userSMIMECertificate"
49
50
51 /* Definition for the context of the cert fetch functions. */
52 struct cert_fetch_context_s
53 {
54   ksba_reader_t reader;  /* The reader used (shallow copy). */
55   unsigned char *tmpbuf; /* Helper buffer.  */
56   size_t tmpbufsize;     /* Allocated size of tmpbuf.  */
57   int truncated;         /* Flag to indicate a truncated output.  */
58 };
59
60
61
62 \f
63 /* Add HOST and PORT to our list of LDAP servers.  Fixme: We should
64    better use an extra list of servers. */
65 static void
66 add_server_to_servers (const char *host, int port)
67 {
68   ldap_server_t server;
69   ldap_server_t last = NULL;
70   const char *s;
71
72   if (!port)
73     port = 389;
74
75   for (server=opt.ldapservers; server; server = server->next)
76     {
77       if (!strcmp (server->host, host) && server->port == port)
78           return; /* already in list... */
79       last = server;
80     }
81
82   /* We assume that the host names are all supplied by our
83      configuration files and thus are sane.  To keep this assumption
84      we must reject all invalid host names. */
85   for (s=host; *s; s++)
86     if (!strchr ("abcdefghijklmnopqrstuvwxyz"
87                  "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
88                  "01234567890.-", *s))
89       {
90         log_error (_("invalid char 0x%02x in host name - not added\n"), *s);
91         return;
92       }
93
94   log_info (_("adding '%s:%d' to the ldap server list\n"), host, port);
95   server = xtrycalloc (1, sizeof *s);
96   if (!server)
97     log_error (_("malloc failed: %s\n"), strerror (errno));
98   else
99     {
100       server->host = xstrdup (host);
101       server->port = port;
102       if (last)
103         last->next = server;
104       else
105         opt.ldapservers = server;
106     }
107 }
108
109
110
111
112 /* Perform an LDAP query.  Returns an gpg error code or 0 on success.
113    The function returns a new reader object at READER. */
114 static gpg_error_t
115 run_ldap_wrapper (ctrl_t ctrl,
116                   int ignore_timeout,
117                   int multi_mode,
118                   const char *proxy,
119                   const char *host, int port,
120                   const char *user, const char *pass,
121                   const char *dn, const char *filter, const char *attr,
122                   const char *url,
123                   ksba_reader_t *reader)
124 {
125   const char *argv[40];
126   int argc;
127   char portbuf[30], timeoutbuf[30];
128
129
130   *reader = NULL;
131
132   argc = 0;
133   if (pass)  /* Note, that the password most be the first item.  */
134     {
135       argv[argc++] = "--pass";
136       argv[argc++] = pass;
137     }
138   if (opt.verbose)
139     argv[argc++] = "-vv";
140   argv[argc++] = "--log-with-pid";
141   if (multi_mode)
142     argv[argc++] = "--multi";
143   if (opt.ldaptimeout)
144     {
145       sprintf (timeoutbuf, "%u", opt.ldaptimeout);
146       argv[argc++] = "--timeout";
147       argv[argc++] = timeoutbuf;
148       if (ignore_timeout)
149         argv[argc++] = "--only-search-timeout";
150     }
151   if (proxy)
152     {
153       argv[argc++] = "--proxy";
154       argv[argc++] = proxy;
155     }
156   if (host)
157     {
158       argv[argc++] = "--host";
159       argv[argc++] = host;
160     }
161   if (port)
162     {
163       sprintf (portbuf, "%d", port);
164       argv[argc++] = "--port";
165       argv[argc++] = portbuf;
166     }
167   if (user)
168     {
169       argv[argc++] = "--user";
170       argv[argc++] = user;
171     }
172   if (dn)
173     {
174       argv[argc++] = "--dn";
175       argv[argc++] = dn;
176     }
177   if (filter)
178     {
179       argv[argc++] = "--filter";
180       argv[argc++] = filter;
181     }
182   if (attr)
183     {
184       argv[argc++] = "--attr";
185       argv[argc++] = attr;
186     }
187   argv[argc++] = url? url : "ldap://";
188   argv[argc] = NULL;
189
190   return ldap_wrapper (ctrl, reader, argv);
191 }
192
193
194
195
196 /* Perform a LDAP query using a given URL. On success a new ksba
197    reader is returned.  If HOST or PORT are not 0, they are used to
198    override the values from the URL. */
199 gpg_error_t
200 url_fetch_ldap (ctrl_t ctrl, const char *url, const char *host, int port,
201                 ksba_reader_t *reader)
202 {
203   gpg_error_t err;
204
205   err = run_ldap_wrapper (ctrl,
206                           1, /* Ignore explicit timeout because CRLs
207                                 might be very large. */
208                           0,
209                           opt.ldap_proxy,
210                           host, port,
211                           NULL, NULL,
212                           NULL, NULL, NULL, url,
213                           reader);
214
215   /* FIXME: This option might be used for DoS attacks.  Because it
216      will enlarge the list of servers to consult without a limit and
217      all LDAP queries w/o a host are will then try each host in
218      turn. */
219   if (!err && opt.add_new_ldapservers && !opt.ldap_proxy)
220     {
221       if (host)
222         add_server_to_servers (host, port);
223       else if (url)
224         {
225           char *tmp = host_and_port_from_url (url, &port);
226           if (tmp)
227             {
228               add_server_to_servers (tmp, port);
229               xfree (tmp);
230             }
231         }
232     }
233
234   /* If the lookup failed and we are not only using the proxy, we try
235      again using our default list of servers.  */
236   if (err && !(opt.ldap_proxy && opt.only_ldap_proxy))
237     {
238       struct ldapserver_iter iter;
239
240       if (DBG_LOOKUP)
241         log_debug ("no hostname in URL or query failed; "
242                    "trying all default hostnames\n");
243
244       for (ldapserver_iter_begin (&iter, ctrl);
245            err && ! ldapserver_iter_end_p (&iter);
246            ldapserver_iter_next (&iter))
247         {
248           ldap_server_t server = iter.server;
249
250           err = run_ldap_wrapper (ctrl,
251                                   0,
252                                   0,
253                                   NULL,
254                                   server->host, server->port,
255                                   NULL, NULL,
256                                   NULL, NULL, NULL, url,
257                                   reader);
258           if (!err)
259             break;
260         }
261     }
262
263   return err;
264 }
265
266
267
268 /* Perform an LDAP query on all configured servers.  On error the
269    error code of the last try is returned.  */
270 gpg_error_t
271 attr_fetch_ldap (ctrl_t ctrl,
272                  const char *dn, const char *attr, ksba_reader_t *reader)
273 {
274   gpg_error_t err = gpg_error (GPG_ERR_CONFIGURATION);
275   struct ldapserver_iter iter;
276
277   *reader = NULL;
278
279   /* FIXME; we might want to look at the Base SN to try matching
280      servers first. */
281   for (ldapserver_iter_begin (&iter, ctrl); ! ldapserver_iter_end_p (&iter);
282        ldapserver_iter_next (&iter))
283     {
284       ldap_server_t server = iter.server;
285
286       err = run_ldap_wrapper (ctrl,
287                               0,
288                               0,
289                               opt.ldap_proxy,
290                               server->host, server->port,
291                               server->user, server->pass,
292                               dn, "objectClass=*", attr, NULL,
293                               reader);
294       if (!err)
295         break; /* Probably found a result. Ready. */
296     }
297   return err;
298 }
299
300 \f
301 /* Parse PATTERN and return a new strlist to be used for the actual
302    LDAP query.  Bit 0 of the flags field is set if that pattern is
303    actually a base specification.  Caller must release the returned
304    strlist.  NULL is returned on error.
305
306  * Possible patterns:
307  *
308  *   KeyID
309  *   Fingerprint
310  *   OpenPGP userid
311  * x Email address  Indicated by a left angle bracket.
312  *   Exact word match in user id or subj. name
313  * x Subj. DN  indicated bu a leading slash
314  *   Issuer DN
315  *   Serial number + subj. DN
316  * x Substring match indicated by a leading '*; is also the default.
317  */
318
319 strlist_t
320 parse_one_pattern (const char *pattern)
321 {
322   strlist_t result = NULL;
323   char *p;
324
325   switch (*pattern)
326     {
327     case '<':                   /* Email. */
328       {
329         pattern++;
330         result = xmalloc (sizeof *result + 5 + strlen (pattern));
331         result->next = NULL;
332         result->flags = 0;
333         p = stpcpy (stpcpy (result->d, "mail="), pattern);
334         if (p[-1] == '>')
335           *--p = 0;
336         if (!*result->d) /* Error. */
337           {
338             xfree (result);
339             result = NULL;
340           }
341         break;
342       }
343     case '/':                   /* Subject DN. */
344       pattern++;
345       if (*pattern)
346         {
347           result = xmalloc (sizeof *result + strlen (pattern));
348           result->next = NULL;
349           result->flags = 1; /* Base spec. */
350           strcpy (result->d, pattern);
351         }
352       break;
353     case '#':                   /* Issuer DN. */
354       pattern++;
355       if (*pattern == '/')  /* Just issuer DN. */
356         {
357           pattern++;
358         }
359       else  /* Serial number + issuer DN */
360         {
361         }
362       break;
363     case '*':
364       pattern++;
365     default:                    /* Take as substring match. */
366       {
367         const char format[] = "(|(sn=*%s*)(|(cn=*%s*)(mail=*%s*)))";
368
369         if (*pattern)
370           {
371             result = xmalloc (sizeof *result
372                               + strlen (format) + 3 * strlen (pattern));
373             result->next = NULL;
374             result->flags = 0;
375             sprintf (result->d, format, pattern, pattern, pattern);
376           }
377       }
378       break;
379     }
380
381   return result;
382 }
383
384 /* Take the string STRING and escape it accoring to the URL rules.
385    Retun a newly allocated string. */
386 static char *
387 escape4url (const char *string)
388 {
389   const char *s;
390   char *buf, *p;
391   size_t n;
392
393   if (!string)
394     string = "";
395
396   for (s=string,n=0; *s; s++)
397     if (strchr (UNENCODED_URL_CHARS, *s))
398       n++;
399     else
400       n += 3;
401
402   buf = malloc (n+1);
403   if (!buf)
404     return NULL;
405
406   for (s=string,p=buf; *s; s++)
407     if (strchr (UNENCODED_URL_CHARS, *s))
408       *p++ = *s;
409     else
410       {
411         sprintf (p, "%%%02X", *(const unsigned char *)s);
412         p += 3;
413       }
414   *p = 0;
415
416   return buf;
417 }
418
419
420
421 /* Create a LDAP URL from DN and FILTER and return it in URL.  We don't
422    need the host and port because this will be specified using the
423    override options. */
424 static gpg_error_t
425 make_url (char **url, const char *dn, const char *filter)
426 {
427   gpg_error_t err;
428   char *u_dn, *u_filter;
429   char const attrs[] = (USERCERTIFICATE ","
430 /*                         USERSMIMECERTIFICATE "," */
431                         CACERTIFICATE ","
432                         X509CACERT );
433
434   *url = NULL;
435
436   u_dn = escape4url (dn);
437   if (!u_dn)
438       return gpg_error_from_errno (errno);
439
440   u_filter = escape4url (filter);
441   if (!u_filter)
442     {
443       err = gpg_error_from_errno (errno);
444       xfree (u_dn);
445       return err;
446     }
447   *url = malloc ( 8 + strlen (u_dn)
448                  + 1 + strlen (attrs)
449                  + 5 + strlen (u_filter) + 1 );
450   if (!*url)
451     {
452       err = gpg_error_from_errno (errno);
453       xfree (u_dn);
454       xfree (u_filter);
455       return err;
456     }
457
458   stpcpy (stpcpy (stpcpy (stpcpy (stpcpy (stpcpy (*url, "ldap:///"),
459                                           u_dn),
460                                   "?"),
461                           attrs),
462                   "?sub?"),
463           u_filter);
464   xfree (u_dn);
465   xfree (u_filter);
466   return 0;
467 }
468
469
470 /* Prepare an LDAP query to return the attribute ATTR for the DN.  All
471    configured default servers are queried until one responds.  This
472    function returns an error code or 0 and a CONTEXT on success. */
473 gpg_error_t
474 start_default_fetch_ldap (ctrl_t ctrl, cert_fetch_context_t *context,
475                           const char *dn, const char *attr)
476 {
477   gpg_error_t err;
478   struct ldapserver_iter iter;
479
480   *context = xtrycalloc (1, sizeof **context);
481   if (!*context)
482     return gpg_error_from_errno (errno);
483
484   /* FIXME; we might want to look at the Base SN to try matching
485      servers first. */
486   err = gpg_error (GPG_ERR_CONFIGURATION);
487
488   for (ldapserver_iter_begin (&iter, ctrl); ! ldapserver_iter_end_p (&iter);
489        ldapserver_iter_next (&iter))
490     {
491       ldap_server_t server = iter.server;
492
493       err = run_ldap_wrapper (ctrl,
494                               0,
495                               1,
496                               opt.ldap_proxy,
497                               server->host, server->port,
498                               server->user, server->pass,
499                               dn, "objectClass=*", attr, NULL,
500                               &(*context)->reader);
501       if (!err)
502         break; /* Probably found a result. */
503     }
504
505   if (err)
506     {
507       xfree (*context);
508       *context = NULL;
509     }
510   return err;
511 }
512
513
514 /* Prepare an LDAP query to return certificates maching PATTERNS using
515    the SERVER.  This function returns an error code or 0 and a CONTEXT
516    on success. */
517 gpg_error_t
518 start_cert_fetch_ldap (ctrl_t ctrl, cert_fetch_context_t *context,
519                        strlist_t patterns, const ldap_server_t server)
520 {
521   gpg_error_t err;
522   const char *host;
523   int port;
524   const char *user;
525   const char *pass;
526   const char *base;
527   const char *argv[50];
528   int argc;
529   char portbuf[30], timeoutbuf[30];
530
531
532   *context = NULL;
533   if (server)
534     {
535       host = server->host;
536       port = server->port;
537       user = server->user;
538       pass = server->pass;
539       base = server->base;
540     }
541   else /* Use a default server. */
542     return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
543
544   if (!base)
545     base = "";
546
547   argc = 0;
548   if (pass) /* Note: Must be the first item. */
549     {
550       argv[argc++] = "--pass";
551       argv[argc++] = pass;
552     }
553   if (opt.verbose)
554     argv[argc++] = "-vv";
555   argv[argc++] = "--log-with-pid";
556   argv[argc++] = "--multi";
557   if (opt.ldaptimeout)
558     {
559       sprintf (timeoutbuf, "%u", opt.ldaptimeout);
560       argv[argc++] = "--timeout";
561       argv[argc++] = timeoutbuf;
562     }
563   if (opt.ldap_proxy)
564     {
565       argv[argc++] = "--proxy";
566       argv[argc++] = opt.ldap_proxy;
567     }
568   if (host)
569     {
570       argv[argc++] = "--host";
571       argv[argc++] = host;
572     }
573   if (port)
574     {
575       sprintf (portbuf, "%d", port);
576       argv[argc++] = "--port";
577       argv[argc++] = portbuf;
578     }
579   if (user)
580     {
581       argv[argc++] = "--user";
582       argv[argc++] = user;
583     }
584
585
586   for (; patterns; patterns = patterns->next)
587     {
588       strlist_t sl;
589       char *url;
590
591       if (argc >= sizeof argv -1)
592         {
593           /* Too many patterns.  It does not make sense to allow an
594              arbitrary number of patters because the length of the
595              command line is limited anyway.  */
596           /* fixme: cleanup. */
597           return gpg_error (GPG_ERR_RESOURCE_LIMIT);
598         }
599       sl = parse_one_pattern (patterns->d);
600       if (!sl)
601         {
602           log_error (_("start_cert_fetch: invalid pattern '%s'\n"),
603                      patterns->d);
604           /* fixme: cleanup argv.  */
605           return gpg_error (GPG_ERR_INV_USER_ID);
606         }
607       if ((sl->flags & 1))
608         err = make_url (&url, sl->d, "objectClass=*");
609       else
610         err = make_url (&url, base, sl->d);
611       free_strlist (sl);
612       if (err)
613         {
614           /* fixme: cleanup argv. */
615           return err;
616         }
617       argv[argc++] = url;
618     }
619   argv[argc] = NULL;
620
621   *context = xtrycalloc (1, sizeof **context);
622   if (!*context)
623     return gpg_error_from_errno (errno);
624
625   err = ldap_wrapper (ctrl, &(*context)->reader, argv);
626
627   if (err)
628     {
629       xfree (*context);
630       *context = NULL;
631     }
632
633   return err;
634 }
635
636
637 /* Read a fixed amount of data from READER into BUFFER.  */
638 static gpg_error_t
639 read_buffer (ksba_reader_t reader, unsigned char *buffer, size_t count)
640 {
641   gpg_error_t err;
642   size_t nread;
643
644   while (count)
645     {
646       err = ksba_reader_read (reader, buffer, count, &nread);
647       if (err)
648         return err;
649       buffer += nread;
650       count -= nread;
651     }
652   return 0;
653 }
654
655
656 /* Fetch the next certificate. Return 0 on success, GPG_ERR_EOF if no
657    (more) certificates are available or any other error
658    code. GPG_ERR_TRUNCATED may be returned to indicate that the result
659    has been truncated. */
660 gpg_error_t
661 fetch_next_cert_ldap (cert_fetch_context_t context,
662                       unsigned char **value, size_t *valuelen)
663 {
664   gpg_error_t err;
665   unsigned char hdr[5];
666   char *p, *pend;
667   int n;
668   int okay = 0;
669   /* int is_cms = 0; */
670
671   *value = NULL;
672   *valuelen = 0;
673
674   err = 0;
675   while (!err)
676     {
677       err = read_buffer (context->reader, hdr, 5);
678       if (err)
679         break;
680       n = (hdr[1] << 24)|(hdr[2]<<16)|(hdr[3]<<8)|hdr[4];
681       if (*hdr == 'V' && okay)
682         {
683 #if 0  /* That code is not yet ready.  */
684
685           if (is_cms)
686             {
687               /* The certificate needs to be parsed from CMS data. */
688               ksba_cms_t cms;
689               ksba_stop_reason_t stopreason;
690               int i;
691
692               err = ksba_cms_new (&cms);
693               if (err)
694                 goto leave;
695               err = ksba_cms_set_reader_writer (cms, context->reader, NULL);
696               if (err)
697                 {
698                   log_error ("ksba_cms_set_reader_writer failed: %s\n",
699                              gpg_strerror (err));
700                   goto leave;
701                 }
702
703               do
704                 {
705                   err = ksba_cms_parse (cms, &stopreason);
706                   if (err)
707                     {
708                       log_error ("ksba_cms_parse failed: %s\n",
709                                  gpg_strerror (err));
710                       goto leave;
711                     }
712
713                   if (stopreason == KSBA_SR_BEGIN_DATA)
714                     log_error ("userSMIMECertificate is not "
715                                "a certs-only message\n");
716                 }
717               while (stopreason != KSBA_SR_READY);
718
719               for (i=0; (cert=ksba_cms_get_cert (cms, i)); i++)
720                 {
721                   check_and_store (ctrl, stats, cert, 0);
722                   ksba_cert_release (cert);
723                   cert = NULL;
724                 }
725               if (!i)
726                 log_error ("no certificate found\n");
727               else
728                 any = 1;
729             }
730           else
731 #endif
732             {
733               *value = xtrymalloc (n);
734               if (!*value)
735                 return gpg_error_from_errno (errno);
736               *valuelen = n;
737               err = read_buffer (context->reader, *value, n);
738               break; /* Ready or error.  */
739             }
740         }
741       else if (!n && *hdr == 'A')
742         okay = 0;
743       else if (n)
744         {
745           if (n > context->tmpbufsize)
746             {
747               xfree (context->tmpbuf);
748               context->tmpbufsize = 0;
749               context->tmpbuf = xtrymalloc (n+1);
750               if (!context->tmpbuf)
751                 return gpg_error_from_errno (errno);
752               context->tmpbufsize = n;
753             }
754           err = read_buffer (context->reader, context->tmpbuf, n);
755           if (err)
756             break;
757           if (*hdr == 'A')
758             {
759               p = context->tmpbuf;
760               p[n] = 0; /*(we allocated one extra byte for this.)*/
761               /* fixme: is_cms = 0; */
762               if ( (pend = strchr (p, ';')) )
763                 *pend = 0; /* Strip off the extension. */
764               if (!ascii_strcasecmp (p, USERCERTIFICATE))
765                 {
766                   if (DBG_LOOKUP)
767                     log_debug ("fetch_next_cert_ldap: got attribute '%s'\n",
768                                USERCERTIFICATE);
769                   okay = 1;
770                 }
771               else if (!ascii_strcasecmp (p, CACERTIFICATE))
772                 {
773                   if (DBG_LOOKUP)
774                     log_debug ("fetch_next_cert_ldap: got attribute '%s'\n",
775                                CACERTIFICATE);
776                   okay = 1;
777                 }
778               else if (!ascii_strcasecmp (p, X509CACERT))
779                 {
780                   if (DBG_LOOKUP)
781                     log_debug ("fetch_next_cert_ldap: got attribute '%s'\n",
782                                CACERTIFICATE);
783                   okay = 1;
784                 }
785 /*               else if (!ascii_strcasecmp (p, USERSMIMECERTIFICATE)) */
786 /*                 { */
787 /*                   if (DBG_LOOKUP) */
788 /*                     log_debug ("fetch_next_cert_ldap: got attribute '%s'\n", */
789 /*                                USERSMIMECERTIFICATE); */
790 /*                   okay = 1; */
791 /*                   is_cms = 1; */
792 /*                 } */
793               else
794                 {
795                   if (DBG_LOOKUP)
796                     log_debug ("fetch_next_cert_ldap: got attribute '%s'"
797                                " -  ignored\n", p);
798                   okay = 0;
799                 }
800             }
801           else if (*hdr == 'E')
802             {
803               p = context->tmpbuf;
804               p[n] = 0; /*(we allocated one extra byte for this.)*/
805               if (!strcmp (p, "truncated"))
806                 {
807                   context->truncated = 1;
808                   log_info (_("ldap_search hit the size limit of"
809                               " the server\n"));
810                 }
811             }
812         }
813     }
814
815   if (err)
816     {
817       xfree (*value);
818       *value = NULL;
819       *valuelen = 0;
820       if (gpg_err_code (err) == GPG_ERR_EOF && context->truncated)
821         {
822           context->truncated = 0; /* So that the next call would return EOF. */
823           err = gpg_error (GPG_ERR_TRUNCATED);
824         }
825     }
826
827   return err;
828 }
829
830
831 void
832 end_cert_fetch_ldap (cert_fetch_context_t context)
833 {
834   if (context)
835     {
836       ksba_reader_t reader = context->reader;
837
838       xfree (context->tmpbuf);
839       xfree (context);
840       ldap_wrapper_release_context (reader);
841       ksba_reader_release (reader);
842     }
843 }