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