Improve documentation for ks_hkp_get.
[gnupg.git] / dirmngr / dirmngr_ldap.c
1 /* dirmngr-ldap.c  -  The LDAP helper for dirmngr.
2  * Copyright (C) 2004 g10 Code GmbH
3  * Copyright (C) 2010 Free Software Foundation, Inc.
4  *
5  * This file is part of GnuPG.
6  *
7  * GnuPG 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 3 of the License, or
10  * (at your option) any later version.
11  *
12  * GnuPG 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 <stdlib.h>
25 #include <stddef.h>
26 #include <stdarg.h>
27 #include <string.h>
28 #ifdef HAVE_SIGNAL_H
29 # include <signal.h>
30 #endif
31 #include <errno.h>
32 #include <assert.h>
33 #include <sys/time.h>
34 #include <unistd.h>
35 #ifndef USE_LDAPWRAPPER
36 # include <npth.h>
37 #endif
38
39 #ifdef HAVE_W32_SYSTEM
40 # include <winsock2.h>
41 # include <winldap.h>
42 # include <winber.h>
43 # include <fcntl.h>
44 # include "ldap-url.h"
45 #else
46   /* For OpenLDAP, to enable the API that we're using. */
47 # define LDAP_DEPRECATED 1
48 # include <ldap.h>
49 #endif
50
51
52 #define JNLIB_NEED_LOG_LOGV
53 #include <gpg-error.h>
54 #include "../common/logging.h"
55 #include "../common/argparse.h"
56 #include "../common/stringhelp.h"
57 #include "../common/mischelp.h"
58 #include "../common/strlist.h"
59
60 #include "i18n.h"
61 #include "util.h"
62 #include "../common/init.h"
63
64 /* With the ldap wrapper, there is no need for the npth_unprotect and leave
65    functions; thus we redefine them to nops.  If we are not using the
66    ldap wrapper process we need to include the prototype for our
67    module's main function.  */
68 #ifdef USE_LDAPWRAPPER
69 static void npth_unprotect (void) { }
70 static void npth_protect (void) { }
71 #else
72 # include "./ldap-wrapper.h"
73 #endif
74
75 #ifdef HAVE_W32CE_SYSTEM
76 # include "w32-ldap-help.h"
77 # define my_ldap_init(a,b)                      \
78   _dirmngr_ldap_init ((a), (b))
79 # define my_ldap_simple_bind_s(a,b,c)           \
80   _dirmngr_ldap_simple_bind_s ((a),(b),(c))
81 # define my_ldap_search_st(a,b,c,d,e,f,g,h)     \
82   _dirmngr_ldap_search_st ((a), (b), (c), (d), (e), (f), (g), (h))
83 # define my_ldap_first_attribute(a,b,c)         \
84   _dirmngr_ldap_first_attribute ((a),(b),(c))
85 # define my_ldap_next_attribute(a,b,c)          \
86   _dirmngr_ldap_next_attribute ((a),(b),(c))
87 # define my_ldap_get_values_len(a,b,c)          \
88   _dirmngr_ldap_get_values_len ((a),(b),(c))
89 # define my_ldap_free_attr(a)                   \
90   xfree ((a))
91 #else
92 # define my_ldap_init(a,b)              ldap_init ((a), (b))
93 # define my_ldap_simple_bind_s(a,b,c)   ldap_simple_bind_s ((a), (b), (c))
94 # define my_ldap_search_st(a,b,c,d,e,f,g,h)     \
95   ldap_search_st ((a), (b), (c), (d), (e), (f), (g), (h))
96 # define my_ldap_first_attribute(a,b,c) ldap_first_attribute ((a),(b),(c))
97 # define my_ldap_next_attribute(a,b,c)  ldap_next_attribute ((a),(b),(c))
98 # define my_ldap_get_values_len(a,b,c)  ldap_get_values_len ((a),(b),(c))
99 # define my_ldap_free_attr(a)           ldap_memfree ((a))
100 #endif
101
102 #ifdef HAVE_W32_SYSTEM
103  typedef LDAP_TIMEVAL  my_ldap_timeval_t;
104 #else
105  typedef struct timeval my_ldap_timeval_t;
106 #endif
107
108 #define DEFAULT_LDAP_TIMEOUT 100 /* Arbitrary long timeout. */
109
110
111 /* Constants for the options.  */
112 enum
113   {
114     oQuiet        = 'q',
115     oVerbose      = 'v',
116
117     oTimeout      = 500,
118     oMulti,
119     oProxy,
120     oHost,
121     oPort,
122     oUser,
123     oPass,
124     oEnvPass,
125     oDN,
126     oFilter,
127     oAttr,
128
129     oOnlySearchTimeout,
130     oLogWithPID
131   };
132
133
134 /* The list of options as used by the argparse.c code.  */
135 static ARGPARSE_OPTS opts[] = {
136   { oVerbose,  "verbose",   0, N_("verbose") },
137   { oQuiet,    "quiet",     0, N_("be somewhat more quiet") },
138   { oTimeout,  "timeout",   1, N_("|N|set LDAP timeout to N seconds")},
139   { oMulti,    "multi",     0, N_("return all values in"
140                                   " a record oriented format")},
141   { oProxy,    "proxy",     2,
142     N_("|NAME|ignore host part and connect through NAME")},
143   { oHost,     "host",      2, N_("|NAME|connect to host NAME")},
144   { oPort,     "port",      1, N_("|N|connect to port N")},
145   { oUser,     "user",      2, N_("|NAME|use user NAME for authentication")},
146   { oPass,     "pass",      2, N_("|PASS|use password PASS"
147                                   " for authentication")},
148   { oEnvPass,  "env-pass",  0, N_("take password from $DIRMNGR_LDAP_PASS")},
149   { oDN,       "dn",        2, N_("|STRING|query DN STRING")},
150   { oFilter,   "filter",    2, N_("|STRING|use STRING as filter expression")},
151   { oAttr,     "attr",      2, N_("|STRING|return the attribute STRING")},
152   { oOnlySearchTimeout, "only-search-timeout", 0, "@"},
153   { oLogWithPID,"log-with-pid", 0, "@"},
154   { 0, NULL, 0, NULL }
155 };
156
157
158 /* A structure with module options.  This is not a static variable
159    because if we are not build as a standalone binary, each thread
160    using this module needs to handle its own values.  */
161 struct my_opt_s
162 {
163   int quiet;
164   int verbose;
165   my_ldap_timeval_t timeout;/* Timeout for the LDAP search functions.  */
166   unsigned int alarm_timeout; /* And for the alarm based timeout.  */
167   int multi;
168
169   estream_t outstream;    /* Send output to thsi stream.  */
170
171   /* Note that we can't use const for the strings because ldap_* are
172      not defined that way.  */
173   char *proxy; /* Host and Port override.  */
174   char *user;  /* Authentication user.  */
175   char *pass;  /* Authentication password.  */
176   char *host;  /* Override host.  */
177   int port;    /* Override port.  */
178   char *dn;    /* Override DN.  */
179   char *filter;/* Override filter.  */
180   char *attr;  /* Override attribute.  */
181 };
182 typedef struct my_opt_s *my_opt_t;
183
184
185 /* Prototypes.  */
186 #ifndef HAVE_W32_SYSTEM
187 static void catch_alarm (int dummy);
188 #endif
189 static int process_url (my_opt_t myopt, const char *url);
190
191
192
193 /* Function called by argparse.c to display information.  */
194 #ifdef USE_LDAPWRAPPER
195 static const char *
196 my_strusage (int level)
197 {
198   const char *p;
199
200   switch(level)
201     {
202     case 11: p = "dirmngr_ldap (@GNUPG@)";
203       break;
204     case 13: p = VERSION; break;
205     case 17: p = PRINTABLE_OS_NAME; break;
206     case 19: p = _("Please report bugs to <@EMAIL@>.\n"); break;
207     case 49: p = PACKAGE_BUGREPORT; break;
208     case 1:
209     case 40: p =
210                _("Usage: dirmngr_ldap [options] [URL] (-h for help)\n");
211       break;
212     case 41: p =
213           _("Syntax: dirmngr_ldap [options] [URL]\n"
214             "Internal LDAP helper for Dirmngr\n"
215             "Interface and options may change without notice\n");
216       break;
217
218     default: p = NULL;
219     }
220   return p;
221 }
222 #endif /*!USE_LDAPWRAPPER*/
223
224
225 int
226 #ifdef USE_LDAPWRAPPER
227 main (int argc, char **argv)
228 #else
229 ldap_wrapper_main (char **argv, estream_t outstream)
230 #endif
231 {
232 #ifndef USE_LDAPWRAPPER
233   int argc;
234 #endif
235   ARGPARSE_ARGS pargs;
236   int any_err = 0;
237   char *p;
238   int only_search_timeout = 0;
239   struct my_opt_s my_opt_buffer;
240   my_opt_t myopt = &my_opt_buffer;
241   char *malloced_buffer1 = NULL;
242
243   memset (&my_opt_buffer, 0, sizeof my_opt_buffer);
244
245   early_system_init ();
246
247 #ifdef USE_LDAPWRAPPER
248   set_strusage (my_strusage);
249   log_set_prefix ("dirmngr_ldap", JNLIB_LOG_WITH_PREFIX);
250
251   /* Setup I18N and common subsystems. */
252   i18n_init();
253
254   init_common_subsystems (&argc, &argv);
255
256   es_set_binary (es_stdout);
257   myopt->outstream = es_stdout;
258 #else /*!USE_LDAPWRAPPER*/
259   myopt->outstream = outstream;
260   for (argc=0; argv[argc]; argc++)
261     ;
262 #endif /*!USE_LDAPWRAPPER*/
263
264   /* LDAP defaults */
265   myopt->timeout.tv_sec = DEFAULT_LDAP_TIMEOUT;
266   myopt->timeout.tv_usec = 0;
267   myopt->alarm_timeout = 0;
268
269   /* Parse the command line.  */
270   pargs.argc = &argc;
271   pargs.argv = &argv;
272   pargs.flags= 1;  /* Do not remove the args. */
273   while (arg_parse (&pargs, opts) )
274     {
275       switch (pargs.r_opt)
276         {
277         case oVerbose: myopt->verbose++; break;
278         case oQuiet: myopt->quiet++; break;
279         case oTimeout:
280           myopt->timeout.tv_sec = pargs.r.ret_int;
281           myopt->timeout.tv_usec = 0;
282           myopt->alarm_timeout = pargs.r.ret_int;
283           break;
284         case oOnlySearchTimeout: only_search_timeout = 1; break;
285         case oMulti: myopt->multi = 1; break;
286         case oUser: myopt->user = pargs.r.ret_str; break;
287         case oPass: myopt->pass = pargs.r.ret_str; break;
288         case oEnvPass:
289           myopt->pass = getenv ("DIRMNGR_LDAP_PASS");
290           break;
291         case oProxy: myopt->proxy = pargs.r.ret_str; break;
292         case oHost: myopt->host = pargs.r.ret_str; break;
293         case oPort: myopt->port = pargs.r.ret_int; break;
294         case oDN:   myopt->dn = pargs.r.ret_str; break;
295         case oFilter: myopt->filter = pargs.r.ret_str; break;
296         case oAttr: myopt->attr = pargs.r.ret_str; break;
297         case oLogWithPID:
298           {
299             unsigned int oldflags;
300             log_get_prefix (&oldflags);
301             log_set_prefix (NULL, oldflags | JNLIB_LOG_WITH_PID);
302           }
303           break;
304
305         default :
306 #ifdef USE_LDAPWRAPPER
307           pargs.err = ARGPARSE_PRINT_ERROR;
308 #else
309           pargs.err = ARGPARSE_PRINT_WARNING;  /* No exit() please.  */
310 #endif
311           break;
312         }
313     }
314
315   if (only_search_timeout)
316     myopt->alarm_timeout = 0;
317
318   if (myopt->proxy)
319     {
320       malloced_buffer1 = xtrystrdup (myopt->proxy);
321       if (!malloced_buffer1)
322         {
323           log_error ("error copying string: %s\n", strerror (errno));
324           return 1;
325         }
326       myopt->host = malloced_buffer1;
327       p = strchr (myopt->host, ':');
328       if (p)
329         {
330           *p++ = 0;
331           myopt->port = atoi (p);
332         }
333       if (!myopt->port)
334         myopt->port = 389;  /* make sure ports gets overridden.  */
335     }
336
337   if (myopt->port < 0 || myopt->port > 65535)
338     log_error (_("invalid port number %d\n"), myopt->port);
339
340 #ifdef USE_LDAPWRAPPER
341   if (log_get_errorcount (0))
342     exit (2);
343   if (argc < 1)
344     usage (1);
345 #else
346   /* All passed arguments should be fine in this case.  */
347   assert (argc);
348 #endif
349
350 #ifdef USE_LDAPWRAPPER
351   if (myopt->alarm_timeout)
352     {
353 #ifndef HAVE_W32_SYSTEM
354 # if defined(HAVE_SIGACTION) && defined(HAVE_STRUCT_SIGACTION)
355       struct sigaction act;
356
357       act.sa_handler = catch_alarm;
358       sigemptyset (&act.sa_mask);
359       act.sa_flags = 0;
360       if (sigaction (SIGALRM,&act,NULL))
361 # else
362       if (signal (SIGALRM, catch_alarm) == SIG_ERR)
363 # endif
364           log_fatal ("unable to register timeout handler\n");
365 #endif
366     }
367 #endif /*USE_LDAPWRAPPER*/
368
369   for (; argc; argc--, argv++)
370     if (process_url (myopt, *argv))
371       any_err = 1;
372
373   xfree (malloced_buffer1);
374   return any_err;
375 }
376
377 #ifndef HAVE_W32_SYSTEM
378 static void
379 catch_alarm (int dummy)
380 {
381   (void)dummy;
382   _exit (10);
383 }
384 #endif
385
386 static void
387 set_timeout (my_opt_t myopt)
388 {
389 #ifdef HAVE_W32_SYSTEM
390   /* FIXME for W32.  */
391   (void)myopt;
392 #else
393   if (myopt->alarm_timeout)
394     alarm (myopt->alarm_timeout);
395 #endif
396 }
397
398
399 /* Helper for fetch_ldap().  */
400 static int
401 print_ldap_entries (my_opt_t myopt, LDAP *ld, LDAPMessage *msg, char *want_attr)
402 {
403   LDAPMessage *item;
404   int any = 0;
405
406   for (npth_unprotect (), item = ldap_first_entry (ld, msg), npth_protect ();
407        item;
408        npth_unprotect (), item = ldap_next_entry (ld, item), npth_protect ())
409     {
410       BerElement *berctx;
411       char *attr;
412
413       if (myopt->verbose > 1)
414         log_info (_("scanning result for attribute '%s'\n"),
415                   want_attr? want_attr : "[all]");
416
417       if (myopt->multi)
418         { /*  Write item marker. */
419           if (es_fwrite ("I\0\0\0\0", 5, 1, myopt->outstream) != 1)
420             {
421               log_error (_("error writing to stdout: %s\n"),
422                          strerror (errno));
423               return -1;
424             }
425         }
426
427
428       for (npth_unprotect (), attr = my_ldap_first_attribute (ld, item, &berctx),
429              npth_protect ();
430            attr;
431            npth_unprotect (), attr = my_ldap_next_attribute (ld, item, berctx),
432              npth_protect ())
433         {
434           struct berval **values;
435           int idx;
436
437           if (myopt->verbose > 1)
438             log_info (_("          available attribute '%s'\n"), attr);
439
440           set_timeout (myopt);
441
442           /* I case we want only one attribute we do a case
443              insensitive compare without the optional extension
444              (i.e. ";binary").  Case insensitive is not really correct
445              but the best we can do.  */
446           if (want_attr)
447             {
448               char *cp1, *cp2;
449               int cmpres;
450
451               cp1 = strchr (want_attr, ';');
452               if (cp1)
453                 *cp1 = 0;
454               cp2 = strchr (attr, ';');
455               if (cp2)
456                 *cp2 = 0;
457               cmpres = ascii_strcasecmp (want_attr, attr);
458               if (cp1)
459                 *cp1 = ';';
460               if (cp2)
461                 *cp2 = ';';
462               if (cmpres)
463                 {
464                   my_ldap_free_attr (attr);
465                   continue; /* Not found:  Try next attribute.  */
466                 }
467             }
468
469           npth_unprotect ();
470           values = my_ldap_get_values_len (ld, item, attr);
471           npth_protect ();
472
473           if (!values)
474             {
475               if (myopt->verbose)
476                 log_info (_("attribute '%s' not found\n"), attr);
477               my_ldap_free_attr (attr);
478               continue;
479             }
480
481           if (myopt->verbose)
482             {
483               log_info (_("found attribute '%s'\n"), attr);
484               if (myopt->verbose > 1)
485                 for (idx=0; values[idx]; idx++)
486                   log_info ("         length[%d]=%d\n",
487                             idx, (int)values[0]->bv_len);
488
489             }
490
491           if (myopt->multi)
492             { /*  Write attribute marker. */
493               unsigned char tmp[5];
494               size_t n = strlen (attr);
495
496               tmp[0] = 'A';
497               tmp[1] = (n >> 24);
498               tmp[2] = (n >> 16);
499               tmp[3] = (n >> 8);
500               tmp[4] = (n);
501               if (es_fwrite (tmp, 5, 1, myopt->outstream) != 1
502                   || es_fwrite (attr, n, 1, myopt->outstream) != 1)
503                 {
504                   log_error (_("error writing to stdout: %s\n"),
505                              strerror (errno));
506                   ldap_value_free_len (values);
507                   my_ldap_free_attr (attr);
508                   ber_free (berctx, 0);
509                   return -1;
510                 }
511             }
512
513           for (idx=0; values[idx]; idx++)
514             {
515               if (myopt->multi)
516                 { /* Write value marker.  */
517                   unsigned char tmp[5];
518                   size_t n = values[0]->bv_len;
519
520                   tmp[0] = 'V';
521                   tmp[1] = (n >> 24);
522                   tmp[2] = (n >> 16);
523                   tmp[3] = (n >> 8);
524                   tmp[4] = (n);
525
526                   if (es_fwrite (tmp, 5, 1, myopt->outstream) != 1)
527                     {
528                       log_error (_("error writing to stdout: %s\n"),
529                                  strerror (errno));
530                       ldap_value_free_len (values);
531                       my_ldap_free_attr (attr);
532                       ber_free (berctx, 0);
533                       return -1;
534                     }
535                 }
536
537               if (es_fwrite (values[0]->bv_val, values[0]->bv_len,
538                              1, myopt->outstream) != 1)
539                 {
540                   log_error (_("error writing to stdout: %s\n"),
541                              strerror (errno));
542                   ldap_value_free_len (values);
543                   my_ldap_free_attr (attr);
544                   ber_free (berctx, 0);
545                   return -1;
546                 }
547
548               any = 1;
549               if (!myopt->multi)
550                 break; /* Print only the first value.  */
551             }
552           ldap_value_free_len (values);
553           my_ldap_free_attr (attr);
554           if (want_attr || !myopt->multi)
555             break; /* We only want to return the first attribute.  */
556         }
557       ber_free (berctx, 0);
558     }
559
560   if (myopt->verbose > 1 && any)
561     log_info ("result has been printed\n");
562
563   return any?0:-1;
564 }
565
566
567
568 /* Helper for the URL based LDAP query. */
569 static int
570 fetch_ldap (my_opt_t myopt, const char *url, const LDAPURLDesc *ludp)
571 {
572   LDAP *ld;
573   LDAPMessage *msg;
574   int rc = 0;
575   char *host, *dn, *filter, *attrs[2], *attr;
576   int port;
577   int ret;
578
579   host     = myopt->host?   myopt->host   : ludp->lud_host;
580   port     = myopt->port?   myopt->port   : ludp->lud_port;
581   dn       = myopt->dn?     myopt->dn     : ludp->lud_dn;
582   filter   = myopt->filter? myopt->filter : ludp->lud_filter;
583   attrs[0] = myopt->attr?   myopt->attr   : ludp->lud_attrs? ludp->lud_attrs[0]:NULL;
584   attrs[1] = NULL;
585   attr = attrs[0];
586
587   if (!port)
588     port = (ludp->lud_scheme && !strcmp (ludp->lud_scheme, "ldaps"))? 636:389;
589
590   if (myopt->verbose)
591     {
592       log_info (_("processing url '%s'\n"), url);
593       if (myopt->user)
594         log_info (_("          user '%s'\n"), myopt->user);
595       if (myopt->pass)
596         log_info (_("          pass '%s'\n"), *myopt->pass?"*****":"");
597       if (host)
598         log_info (_("          host '%s'\n"), host);
599       log_info (_("          port %d\n"), port);
600       if (dn)
601         log_info (_("            DN '%s'\n"), dn);
602       if (filter)
603         log_info (_("        filter '%s'\n"), filter);
604       if (myopt->multi && !myopt->attr && ludp->lud_attrs)
605         {
606           int i;
607           for (i=0; ludp->lud_attrs[i]; i++)
608             log_info (_("          attr '%s'\n"), ludp->lud_attrs[i]);
609         }
610       else if (attr)
611         log_info (_("          attr '%s'\n"), attr);
612     }
613
614
615   if (!host || !*host)
616     {
617       log_error (_("no host name in '%s'\n"), url);
618       return -1;
619     }
620   if (!myopt->multi && !attr)
621     {
622       log_error (_("no attribute given for query '%s'\n"), url);
623       return -1;
624     }
625
626   if (!myopt->multi && !myopt->attr
627       && ludp->lud_attrs && ludp->lud_attrs[0] && ludp->lud_attrs[1])
628     log_info (_("WARNING: using first attribute only\n"));
629
630
631   set_timeout (myopt);
632   npth_unprotect ();
633   ld = my_ldap_init (host, port);
634   npth_protect ();
635   if (!ld)
636     {
637       log_error (_("LDAP init to '%s:%d' failed: %s\n"),
638                  host, port, strerror (errno));
639       return -1;
640     }
641   npth_unprotect ();
642   /* Fixme:  Can we use MYOPT->user or is it shared with other theeads?.  */
643   ret = my_ldap_simple_bind_s (ld, myopt->user, myopt->pass);
644   npth_protect ();
645   if (ret)
646     {
647       log_error (_("binding to '%s:%d' failed: %s\n"),
648                  host, port, strerror (errno));
649       ldap_unbind (ld);
650       return -1;
651     }
652
653   set_timeout (myopt);
654   npth_unprotect ();
655   rc = my_ldap_search_st (ld, dn, ludp->lud_scope, filter,
656                           myopt->multi && !myopt->attr && ludp->lud_attrs?
657                           ludp->lud_attrs:attrs,
658                           0,
659                           &myopt->timeout, &msg);
660   npth_protect ();
661   if (rc == LDAP_SIZELIMIT_EXCEEDED && myopt->multi)
662     {
663       if (es_fwrite ("E\0\0\0\x09truncated", 14, 1, myopt->outstream) != 1)
664         {
665           log_error (_("error writing to stdout: %s\n"), strerror (errno));
666           return -1;
667         }
668     }
669   else if (rc)
670     {
671 #ifdef HAVE_W32CE_SYSTEM
672       log_error ("searching '%s' failed: %d\n", url, rc);
673 #else
674       log_error (_("searching '%s' failed: %s\n"),
675                  url, ldap_err2string (rc));
676 #endif
677       if (rc != LDAP_NO_SUCH_OBJECT)
678         {
679           /* FIXME: Need deinit (ld)?  */
680           /* Hmmm: Do we need to released MSG in case of an error? */
681           return -1;
682         }
683     }
684
685   rc = print_ldap_entries (myopt, ld, msg, myopt->multi? NULL:attr);
686
687   ldap_msgfree (msg);
688   ldap_unbind (ld);
689   return rc;
690 }
691
692
693
694
695 /* Main processing.  Take the URL and run the LDAP query. The result
696    is printed to stdout, errors are logged to the log stream. */
697 static int
698 process_url (my_opt_t myopt, const char *url)
699 {
700   int rc;
701   LDAPURLDesc *ludp = NULL;
702
703
704   if (!ldap_is_ldap_url (url))
705     {
706       log_error (_("'%s' is not an LDAP URL\n"), url);
707       return -1;
708     }
709
710   if (ldap_url_parse (url, &ludp))
711     {
712       log_error (_("'%s' is an invalid LDAP URL\n"), url);
713       return -1;
714     }
715
716   rc = fetch_ldap (myopt, url, ludp);
717
718   ldap_free_urldesc (ludp);
719   return rc;
720 }