dirmngr_ldap.c 20.4 KB
Newer Older
Werner Koch's avatar
Werner Koch committed
1
/* dirmngr-ldap.c  -  The LDAP helper for dirmngr.
Werner Koch's avatar
Werner Koch committed
2 3
 * Copyright (C) 2004 g10 Code GmbH
 * Copyright (C) 2010 Free Software Foundation, Inc.
Werner Koch's avatar
Werner Koch committed
4
 *
Werner Koch's avatar
Werner Koch committed
5
 * This file is part of GnuPG.
Werner Koch's avatar
Werner Koch committed
6
 *
Werner Koch's avatar
Werner Koch committed
7
 * GnuPG is free software; you can redistribute it and/or modify
Werner Koch's avatar
Werner Koch committed
8
 * it under the terms of the GNU General Public License as published by
Werner Koch's avatar
Werner Koch committed
9
 * the Free Software Foundation; either version 3 of the License, or
Werner Koch's avatar
Werner Koch committed
10 11
 * (at your option) any later version.
 *
Werner Koch's avatar
Werner Koch committed
12
 * GnuPG is distributed in the hope that it will be useful,
Werner Koch's avatar
Werner Koch committed
13 14 15 16 17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * 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/>.
Werner Koch's avatar
Werner Koch committed
19 20 21 22 23 24 25 26 27
 */

#include <config.h>

#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <stdarg.h>
#include <string.h>
28 29 30
#ifdef HAVE_SIGNAL_H
# include <signal.h>
#endif
Werner Koch's avatar
Werner Koch committed
31 32 33 34
#include <errno.h>
#include <assert.h>
#include <sys/time.h>
#include <unistd.h>
35
#ifndef USE_LDAPWRAPPER
Marcus Brinkmann's avatar
Marcus Brinkmann committed
36
# include <npth.h>
37
#endif
Werner Koch's avatar
Werner Koch committed
38 39

#ifdef HAVE_W32_SYSTEM
40 41 42 43 44
# include <winsock2.h>
# include <winldap.h>
# include <winber.h>
# include <fcntl.h>
# include "ldap-url.h"
Werner Koch's avatar
Werner Koch committed
45
#else
46 47 48
  /* For OpenLDAP, to enable the API that we're using. */
# define LDAP_DEPRECATED 1
# include <ldap.h>
Werner Koch's avatar
Werner Koch committed
49 50 51
#endif


52
#include <gpg-error.h>
Werner Koch's avatar
Werner Koch committed
53 54 55 56 57 58 59 60
#include "../common/logging.h"
#include "../common/argparse.h"
#include "../common/stringhelp.h"
#include "../common/mischelp.h"
#include "../common/strlist.h"

#include "i18n.h"
#include "util.h"
61
#include "../common/init.h"
Werner Koch's avatar
Werner Koch committed
62

63
/* With the ldap wrapper, there is no need for the npth_unprotect and leave
64 65 66 67
   functions; thus we redefine them to nops.  If we are not using the
   ldap wrapper process we need to include the prototype for our
   module's main function.  */
#ifdef USE_LDAPWRAPPER
68 69
static void npth_unprotect (void) { }
static void npth_protect (void) { }
70 71
#else
# include "./ldap-wrapper.h"
Werner Koch's avatar
Werner Koch committed
72 73
#endif

Werner Koch's avatar
Werner Koch committed
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
#ifdef HAVE_W32CE_SYSTEM
# include "w32-ldap-help.h"
# define my_ldap_init(a,b)                      \
  _dirmngr_ldap_init ((a), (b))
# define my_ldap_simple_bind_s(a,b,c)           \
  _dirmngr_ldap_simple_bind_s ((a),(b),(c))
# define my_ldap_search_st(a,b,c,d,e,f,g,h)     \
  _dirmngr_ldap_search_st ((a), (b), (c), (d), (e), (f), (g), (h))
# define my_ldap_first_attribute(a,b,c)         \
  _dirmngr_ldap_first_attribute ((a),(b),(c))
# define my_ldap_next_attribute(a,b,c)          \
  _dirmngr_ldap_next_attribute ((a),(b),(c))
# define my_ldap_get_values_len(a,b,c)          \
  _dirmngr_ldap_get_values_len ((a),(b),(c))
# define my_ldap_free_attr(a)                   \
  xfree ((a))
#else
# define my_ldap_init(a,b)              ldap_init ((a), (b))
# define my_ldap_simple_bind_s(a,b,c)   ldap_simple_bind_s ((a), (b), (c))
# define my_ldap_search_st(a,b,c,d,e,f,g,h)     \
  ldap_search_st ((a), (b), (c), (d), (e), (f), (g), (h))
# define my_ldap_first_attribute(a,b,c) ldap_first_attribute ((a),(b),(c))
# define my_ldap_next_attribute(a,b,c)  ldap_next_attribute ((a),(b),(c))
# define my_ldap_get_values_len(a,b,c)  ldap_get_values_len ((a),(b),(c))
# define my_ldap_free_attr(a)           ldap_memfree ((a))
#endif

101 102 103 104 105 106
#ifdef HAVE_W32_SYSTEM
 typedef LDAP_TIMEVAL  my_ldap_timeval_t;
#else
 typedef struct timeval my_ldap_timeval_t;
#endif

Werner Koch's avatar
Werner Koch committed
107 108 109 110
#define DEFAULT_LDAP_TIMEOUT 100 /* Arbitrary long timeout. */


/* Constants for the options.  */
111
enum
Werner Koch's avatar
Werner Koch committed
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
  {
    oQuiet	  = 'q',
    oVerbose	  = 'v',

    oTimeout      = 500,
    oMulti,
    oProxy,
    oHost,
    oPort,
    oUser,
    oPass,
    oEnvPass,
    oDN,
    oFilter,
    oAttr,

    oOnlySearchTimeout,
    oLogWithPID
  };


/* The list of options as used by the argparse.c code.  */
static ARGPARSE_OPTS opts[] = {
  { oVerbose,  "verbose",   0, N_("verbose") },
  { oQuiet,    "quiet",     0, N_("be somewhat more quiet") },
  { oTimeout,  "timeout",   1, N_("|N|set LDAP timeout to N seconds")},
  { oMulti,    "multi",     0, N_("return all values in"
                                  " a record oriented format")},
  { oProxy,    "proxy",     2,
    N_("|NAME|ignore host part and connect through NAME")},
  { oHost,     "host",      2, N_("|NAME|connect to host NAME")},
  { oPort,     "port",      1, N_("|N|connect to port N")},
  { oUser,     "user",      2, N_("|NAME|use user NAME for authentication")},
  { oPass,     "pass",      2, N_("|PASS|use password PASS"
                                  " for authentication")},
  { oEnvPass,  "env-pass",  0, N_("take password from $DIRMNGR_LDAP_PASS")},
  { oDN,       "dn",        2, N_("|STRING|query DN STRING")},
  { oFilter,   "filter",    2, N_("|STRING|use STRING as filter expression")},
  { oAttr,     "attr",      2, N_("|STRING|return the attribute STRING")},
  { oOnlySearchTimeout, "only-search-timeout", 0, "@"},
  { oLogWithPID,"log-with-pid", 0, "@"},
  { 0, NULL, 0, NULL }
};


Werner Koch's avatar
Werner Koch committed
157 158 159 160
/* A structure with module options.  This is not a static variable
   because if we are not build as a standalone binary, each thread
   using this module needs to handle its own values.  */
struct my_opt_s
Werner Koch's avatar
Werner Koch committed
161 162 163
{
  int quiet;
  int verbose;
164
  my_ldap_timeval_t timeout;/* Timeout for the LDAP search functions.  */
Werner Koch's avatar
Werner Koch committed
165 166 167
  unsigned int alarm_timeout; /* And for the alarm based timeout.  */
  int multi;

168
  estream_t outstream;    /* Send output to this stream.  */
Werner Koch's avatar
Werner Koch committed
169

Werner Koch's avatar
Werner Koch committed
170 171 172 173 174 175 176 177 178 179
  /* Note that we can't use const for the strings because ldap_* are
     not defined that way.  */
  char *proxy; /* Host and Port override.  */
  char *user;  /* Authentication user.  */
  char *pass;  /* Authentication password.  */
  char *host;  /* Override host.  */
  int port;    /* Override port.  */
  char *dn;    /* Override DN.  */
  char *filter;/* Override filter.  */
  char *attr;  /* Override attribute.  */
Werner Koch's avatar
Werner Koch committed
180
};
181
typedef struct my_opt_s *my_opt_t;
Werner Koch's avatar
Werner Koch committed
182 183 184


/* Prototypes.  */
Werner Koch's avatar
Werner Koch committed
185
#ifndef HAVE_W32_SYSTEM
Werner Koch's avatar
Werner Koch committed
186
static void catch_alarm (int dummy);
Werner Koch's avatar
Werner Koch committed
187
#endif
Werner Koch's avatar
Werner Koch committed
188
static int process_url (my_opt_t myopt, const char *url);
Werner Koch's avatar
Werner Koch committed
189 190 191 192



/* Function called by argparse.c to display information.  */
Werner Koch's avatar
Werner Koch committed
193
#ifdef USE_LDAPWRAPPER
Werner Koch's avatar
Werner Koch committed
194 195 196 197
static const char *
my_strusage (int level)
{
  const char *p;
198

Werner Koch's avatar
Werner Koch committed
199 200
  switch(level)
    {
201
    case 11: p = "dirmngr_ldap (@GNUPG@)";
Werner Koch's avatar
Werner Koch committed
202 203 204 205 206 207 208 209 210 211 212
      break;
    case 13: p = VERSION; break;
    case 17: p = PRINTABLE_OS_NAME; break;
    case 19: p = _("Please report bugs to <@EMAIL@>.\n"); break;
    case 49: p = PACKAGE_BUGREPORT; break;
    case 1:
    case 40: p =
               _("Usage: dirmngr_ldap [options] [URL] (-h for help)\n");
      break;
    case 41: p =
          _("Syntax: dirmngr_ldap [options] [URL]\n"
213 214
            "Internal LDAP helper for Dirmngr\n"
            "Interface and options may change without notice\n");
Werner Koch's avatar
Werner Koch committed
215 216 217 218 219 220
      break;

    default: p = NULL;
    }
  return p;
}
221
#endif /*!USE_LDAPWRAPPER*/
Werner Koch's avatar
Werner Koch committed
222 223 224


int
Werner Koch's avatar
Werner Koch committed
225 226 227 228 229
#ifdef USE_LDAPWRAPPER
main (int argc, char **argv)
#else
ldap_wrapper_main (char **argv, estream_t outstream)
#endif
Werner Koch's avatar
Werner Koch committed
230
{
Werner Koch's avatar
Werner Koch committed
231 232 233
#ifndef USE_LDAPWRAPPER
  int argc;
#endif
Werner Koch's avatar
Werner Koch committed
234 235 236 237
  ARGPARSE_ARGS pargs;
  int any_err = 0;
  char *p;
  int only_search_timeout = 0;
Werner Koch's avatar
Werner Koch committed
238 239
  struct my_opt_s my_opt_buffer;
  my_opt_t myopt = &my_opt_buffer;
Werner Koch's avatar
Werner Koch committed
240
  char *malloced_buffer1 = NULL;
241

Werner Koch's avatar
Werner Koch committed
242
  memset (&my_opt_buffer, 0, sizeof my_opt_buffer);
Werner Koch's avatar
Werner Koch committed
243

244 245
  early_system_init ();

Werner Koch's avatar
Werner Koch committed
246
#ifdef USE_LDAPWRAPPER
Werner Koch's avatar
Werner Koch committed
247
  set_strusage (my_strusage);
248
  log_set_prefix ("dirmngr_ldap", GPGRT_LOG_WITH_PREFIX);
249

250
  /* Setup I18N and common subsystems. */
251
  i18n_init();
Werner Koch's avatar
Werner Koch committed
252

253 254 255
  init_common_subsystems (&argc, &argv);

  es_set_binary (es_stdout);
Werner Koch's avatar
Werner Koch committed
256 257 258 259 260 261
  myopt->outstream = es_stdout;
#else /*!USE_LDAPWRAPPER*/
  myopt->outstream = outstream;
  for (argc=0; argv[argc]; argc++)
    ;
#endif /*!USE_LDAPWRAPPER*/
262

Werner Koch's avatar
Werner Koch committed
263
  /* LDAP defaults */
Werner Koch's avatar
Werner Koch committed
264 265 266
  myopt->timeout.tv_sec = DEFAULT_LDAP_TIMEOUT;
  myopt->timeout.tv_usec = 0;
  myopt->alarm_timeout = 0;
Werner Koch's avatar
Werner Koch committed
267 268 269 270 271 272 273 274 275

  /* Parse the command line.  */
  pargs.argc = &argc;
  pargs.argv = &argv;
  pargs.flags= 1;  /* Do not remove the args. */
  while (arg_parse (&pargs, opts) )
    {
      switch (pargs.r_opt)
        {
Werner Koch's avatar
Werner Koch committed
276 277
        case oVerbose: myopt->verbose++; break;
        case oQuiet: myopt->quiet++; break;
278 279
	case oTimeout:
	  myopt->timeout.tv_sec = pargs.r.ret_int;
Werner Koch's avatar
Werner Koch committed
280 281
	  myopt->timeout.tv_usec = 0;
          myopt->alarm_timeout = pargs.r.ret_int;
Werner Koch's avatar
Werner Koch committed
282 283
	  break;
        case oOnlySearchTimeout: only_search_timeout = 1; break;
Werner Koch's avatar
Werner Koch committed
284 285 286
        case oMulti: myopt->multi = 1; break;
        case oUser: myopt->user = pargs.r.ret_str; break;
        case oPass: myopt->pass = pargs.r.ret_str; break;
Werner Koch's avatar
Werner Koch committed
287
        case oEnvPass:
Werner Koch's avatar
Werner Koch committed
288
          myopt->pass = getenv ("DIRMNGR_LDAP_PASS");
Werner Koch's avatar
Werner Koch committed
289
          break;
Werner Koch's avatar
Werner Koch committed
290 291 292 293 294 295
        case oProxy: myopt->proxy = pargs.r.ret_str; break;
        case oHost: myopt->host = pargs.r.ret_str; break;
        case oPort: myopt->port = pargs.r.ret_int; break;
        case oDN:   myopt->dn = pargs.r.ret_str; break;
        case oFilter: myopt->filter = pargs.r.ret_str; break;
        case oAttr: myopt->attr = pargs.r.ret_str; break;
Werner Koch's avatar
Werner Koch committed
296 297 298 299
        case oLogWithPID:
          {
            unsigned int oldflags;
            log_get_prefix (&oldflags);
300
            log_set_prefix (NULL, oldflags | GPGRT_LOG_WITH_PID);
Werner Koch's avatar
Werner Koch committed
301 302 303
          }
          break;

Werner Koch's avatar
Werner Koch committed
304 305 306 307 308 309 310
        default :
#ifdef USE_LDAPWRAPPER
          pargs.err = ARGPARSE_PRINT_ERROR;
#else
          pargs.err = ARGPARSE_PRINT_WARNING;  /* No exit() please.  */
#endif
          break;
Werner Koch's avatar
Werner Koch committed
311 312 313 314
	}
    }

  if (only_search_timeout)
Werner Koch's avatar
Werner Koch committed
315
    myopt->alarm_timeout = 0;
Werner Koch's avatar
Werner Koch committed
316

Werner Koch's avatar
Werner Koch committed
317
  if (myopt->proxy)
Werner Koch's avatar
Werner Koch committed
318
    {
Werner Koch's avatar
Werner Koch committed
319 320 321 322 323 324 325
      malloced_buffer1 = xtrystrdup (myopt->proxy);
      if (!malloced_buffer1)
        {
          log_error ("error copying string: %s\n", strerror (errno));
          return 1;
        }
      myopt->host = malloced_buffer1;
Werner Koch's avatar
Werner Koch committed
326
      p = strchr (myopt->host, ':');
Werner Koch's avatar
Werner Koch committed
327 328 329
      if (p)
        {
          *p++ = 0;
Werner Koch's avatar
Werner Koch committed
330
          myopt->port = atoi (p);
Werner Koch's avatar
Werner Koch committed
331
        }
Werner Koch's avatar
Werner Koch committed
332 333
      if (!myopt->port)
        myopt->port = 389;  /* make sure ports gets overridden.  */
Werner Koch's avatar
Werner Koch committed
334
    }
335

Werner Koch's avatar
Werner Koch committed
336 337
  if (myopt->port < 0 || myopt->port > 65535)
    log_error (_("invalid port number %d\n"), myopt->port);
Werner Koch's avatar
Werner Koch committed
338

Werner Koch's avatar
Werner Koch committed
339
#ifdef USE_LDAPWRAPPER
Werner Koch's avatar
Werner Koch committed
340 341 342 343
  if (log_get_errorcount (0))
    exit (2);
  if (argc < 1)
    usage (1);
Werner Koch's avatar
Werner Koch committed
344 345 346 347
#else
  /* All passed arguments should be fine in this case.  */
  assert (argc);
#endif
Werner Koch's avatar
Werner Koch committed
348

Werner Koch's avatar
Werner Koch committed
349 350
#ifdef USE_LDAPWRAPPER
  if (myopt->alarm_timeout)
Werner Koch's avatar
Werner Koch committed
351 352 353 354
    {
#ifndef HAVE_W32_SYSTEM
# if defined(HAVE_SIGACTION) && defined(HAVE_STRUCT_SIGACTION)
      struct sigaction act;
355

Werner Koch's avatar
Werner Koch committed
356 357 358 359
      act.sa_handler = catch_alarm;
      sigemptyset (&act.sa_mask);
      act.sa_flags = 0;
      if (sigaction (SIGALRM,&act,NULL))
360
# else
Werner Koch's avatar
Werner Koch committed
361 362 363 364 365
      if (signal (SIGALRM, catch_alarm) == SIG_ERR)
# endif
          log_fatal ("unable to register timeout handler\n");
#endif
    }
Werner Koch's avatar
Werner Koch committed
366
#endif /*USE_LDAPWRAPPER*/
Werner Koch's avatar
Werner Koch committed
367 368

  for (; argc; argc--, argv++)
Werner Koch's avatar
Werner Koch committed
369
    if (process_url (myopt, *argv))
Werner Koch's avatar
Werner Koch committed
370 371
      any_err = 1;

Werner Koch's avatar
Werner Koch committed
372
  xfree (malloced_buffer1);
Werner Koch's avatar
Werner Koch committed
373 374 375
  return any_err;
}

Werner Koch's avatar
Werner Koch committed
376
#ifndef HAVE_W32_SYSTEM
Werner Koch's avatar
Werner Koch committed
377 378 379 380 381 382
static void
catch_alarm (int dummy)
{
  (void)dummy;
  _exit (10);
}
Werner Koch's avatar
Werner Koch committed
383
#endif
Werner Koch's avatar
Werner Koch committed
384 385

static void
Werner Koch's avatar
Werner Koch committed
386
set_timeout (my_opt_t myopt)
Werner Koch's avatar
Werner Koch committed
387
{
388
#ifdef HAVE_W32_SYSTEM
Werner Koch's avatar
Werner Koch committed
389
  /* FIXME for W32.  */
390 391
  (void)myopt;
#else
Werner Koch's avatar
Werner Koch committed
392 393
  if (myopt->alarm_timeout)
    alarm (myopt->alarm_timeout);
Werner Koch's avatar
Werner Koch committed
394 395 396 397 398 399
#endif
}


/* Helper for fetch_ldap().  */
static int
Werner Koch's avatar
Werner Koch committed
400
print_ldap_entries (my_opt_t myopt, LDAP *ld, LDAPMessage *msg, char *want_attr)
Werner Koch's avatar
Werner Koch committed
401 402 403 404
{
  LDAPMessage *item;
  int any = 0;

405
  for (npth_unprotect (), item = ldap_first_entry (ld, msg), npth_protect ();
406
       item;
407
       npth_unprotect (), item = ldap_next_entry (ld, item), npth_protect ())
Werner Koch's avatar
Werner Koch committed
408 409 410 411
    {
      BerElement *berctx;
      char *attr;

Werner Koch's avatar
Werner Koch committed
412
      if (myopt->verbose > 1)
413
        log_info (_("scanning result for attribute '%s'\n"),
Werner Koch's avatar
Werner Koch committed
414 415
                  want_attr? want_attr : "[all]");

Werner Koch's avatar
Werner Koch committed
416
      if (myopt->multi)
Werner Koch's avatar
Werner Koch committed
417
        { /*  Write item marker. */
418
          if (es_fwrite ("I\0\0\0\0", 5, 1, myopt->outstream) != 1)
Werner Koch's avatar
Werner Koch committed
419 420 421 422 423 424 425
            {
              log_error (_("error writing to stdout: %s\n"),
                         strerror (errno));
              return -1;
            }
        }

426

427 428
      for (npth_unprotect (), attr = my_ldap_first_attribute (ld, item, &berctx),
             npth_protect ();
429
           attr;
430 431
           npth_unprotect (), attr = my_ldap_next_attribute (ld, item, berctx),
             npth_protect ())
Werner Koch's avatar
Werner Koch committed
432 433 434 435
        {
          struct berval **values;
          int idx;

Werner Koch's avatar
Werner Koch committed
436
          if (myopt->verbose > 1)
437
            log_info (_("          available attribute '%s'\n"), attr);
438

Werner Koch's avatar
Werner Koch committed
439
          set_timeout (myopt);
Werner Koch's avatar
Werner Koch committed
440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462

          /* I case we want only one attribute we do a case
             insensitive compare without the optional extension
             (i.e. ";binary").  Case insensitive is not really correct
             but the best we can do.  */
          if (want_attr)
            {
              char *cp1, *cp2;
              int cmpres;

              cp1 = strchr (want_attr, ';');
              if (cp1)
                *cp1 = 0;
              cp2 = strchr (attr, ';');
              if (cp2)
                *cp2 = 0;
              cmpres = ascii_strcasecmp (want_attr, attr);
              if (cp1)
                *cp1 = ';';
              if (cp2)
                *cp2 = ';';
              if (cmpres)
                {
Werner Koch's avatar
Werner Koch committed
463
                  my_ldap_free_attr (attr);
Werner Koch's avatar
Werner Koch committed
464 465 466 467
                  continue; /* Not found:  Try next attribute.  */
                }
            }

468
          npth_unprotect ();
Werner Koch's avatar
Werner Koch committed
469
          values = my_ldap_get_values_len (ld, item, attr);
470
          npth_protect ();
471

Werner Koch's avatar
Werner Koch committed
472 473
          if (!values)
            {
Werner Koch's avatar
Werner Koch committed
474
              if (myopt->verbose)
475
                log_info (_("attribute '%s' not found\n"), attr);
Werner Koch's avatar
Werner Koch committed
476
              my_ldap_free_attr (attr);
Werner Koch's avatar
Werner Koch committed
477 478 479
              continue;
            }

Werner Koch's avatar
Werner Koch committed
480
          if (myopt->verbose)
Werner Koch's avatar
Werner Koch committed
481
            {
482
              log_info (_("found attribute '%s'\n"), attr);
Werner Koch's avatar
Werner Koch committed
483
              if (myopt->verbose > 1)
Werner Koch's avatar
Werner Koch committed
484 485 486
                for (idx=0; values[idx]; idx++)
                  log_info ("         length[%d]=%d\n",
                            idx, (int)values[0]->bv_len);
487

Werner Koch's avatar
Werner Koch committed
488 489
            }

Werner Koch's avatar
Werner Koch committed
490
          if (myopt->multi)
Werner Koch's avatar
Werner Koch committed
491 492 493 494 495 496 497 498 499
            { /*  Write attribute marker. */
              unsigned char tmp[5];
              size_t n = strlen (attr);

              tmp[0] = 'A';
              tmp[1] = (n >> 24);
              tmp[2] = (n >> 16);
              tmp[3] = (n >> 8);
              tmp[4] = (n);
500
              if (es_fwrite (tmp, 5, 1, myopt->outstream) != 1
501
                  || es_fwrite (attr, n, 1, myopt->outstream) != 1)
Werner Koch's avatar
Werner Koch committed
502 503 504 505
                {
                  log_error (_("error writing to stdout: %s\n"),
                             strerror (errno));
                  ldap_value_free_len (values);
Werner Koch's avatar
Werner Koch committed
506
                  my_ldap_free_attr (attr);
Werner Koch's avatar
Werner Koch committed
507 508 509 510 511 512 513
                  ber_free (berctx, 0);
                  return -1;
                }
            }

          for (idx=0; values[idx]; idx++)
            {
Werner Koch's avatar
Werner Koch committed
514
              if (myopt->multi)
Werner Koch's avatar
Werner Koch committed
515 516 517 518 519 520 521 522 523 524
                { /* Write value marker.  */
                  unsigned char tmp[5];
                  size_t n = values[0]->bv_len;

                  tmp[0] = 'V';
                  tmp[1] = (n >> 24);
                  tmp[2] = (n >> 16);
                  tmp[3] = (n >> 8);
                  tmp[4] = (n);

525
                  if (es_fwrite (tmp, 5, 1, myopt->outstream) != 1)
Werner Koch's avatar
Werner Koch committed
526 527 528 529
                    {
                      log_error (_("error writing to stdout: %s\n"),
                                 strerror (errno));
                      ldap_value_free_len (values);
Werner Koch's avatar
Werner Koch committed
530
                      my_ldap_free_attr (attr);
Werner Koch's avatar
Werner Koch committed
531 532 533 534
                      ber_free (berctx, 0);
                      return -1;
                    }
                }
535

536
	      if (es_fwrite (values[0]->bv_val, values[0]->bv_len,
537
                             1, myopt->outstream) != 1)
Werner Koch's avatar
Werner Koch committed
538 539 540 541
                {
                  log_error (_("error writing to stdout: %s\n"),
                             strerror (errno));
                  ldap_value_free_len (values);
Werner Koch's avatar
Werner Koch committed
542
                  my_ldap_free_attr (attr);
Werner Koch's avatar
Werner Koch committed
543 544 545
                  ber_free (berctx, 0);
                  return -1;
                }
546

Werner Koch's avatar
Werner Koch committed
547
              any = 1;
Werner Koch's avatar
Werner Koch committed
548
              if (!myopt->multi)
Werner Koch's avatar
Werner Koch committed
549 550 551
                break; /* Print only the first value.  */
            }
          ldap_value_free_len (values);
Werner Koch's avatar
Werner Koch committed
552
          my_ldap_free_attr (attr);
Werner Koch's avatar
Werner Koch committed
553
          if (want_attr || !myopt->multi)
Werner Koch's avatar
Werner Koch committed
554 555 556
            break; /* We only want to return the first attribute.  */
        }
      ber_free (berctx, 0);
557
    }
Werner Koch's avatar
Werner Koch committed
558

Werner Koch's avatar
Werner Koch committed
559
  if (myopt->verbose > 1 && any)
Werner Koch's avatar
Werner Koch committed
560 561 562 563 564 565 566 567 568
    log_info ("result has been printed\n");

  return any?0:-1;
}



/* Helper for the URL based LDAP query. */
static int
Werner Koch's avatar
Werner Koch committed
569
fetch_ldap (my_opt_t myopt, const char *url, const LDAPURLDesc *ludp)
Werner Koch's avatar
Werner Koch committed
570 571 572 573 574 575
{
  LDAP *ld;
  LDAPMessage *msg;
  int rc = 0;
  char *host, *dn, *filter, *attrs[2], *attr;
  int port;
576
  int ret;
Werner Koch's avatar
Werner Koch committed
577

Werner Koch's avatar
Werner Koch committed
578 579 580 581 582
  host     = myopt->host?   myopt->host   : ludp->lud_host;
  port     = myopt->port?   myopt->port   : ludp->lud_port;
  dn       = myopt->dn?     myopt->dn     : ludp->lud_dn;
  filter   = myopt->filter? myopt->filter : ludp->lud_filter;
  attrs[0] = myopt->attr?   myopt->attr   : ludp->lud_attrs? ludp->lud_attrs[0]:NULL;
Werner Koch's avatar
Werner Koch committed
583 584 585 586 587 588
  attrs[1] = NULL;
  attr = attrs[0];

  if (!port)
    port = (ludp->lud_scheme && !strcmp (ludp->lud_scheme, "ldaps"))? 636:389;

Werner Koch's avatar
Werner Koch committed
589
  if (myopt->verbose)
Werner Koch's avatar
Werner Koch committed
590
    {
591
      log_info (_("processing url '%s'\n"), url);
Werner Koch's avatar
Werner Koch committed
592
      if (myopt->user)
593
        log_info (_("          user '%s'\n"), myopt->user);
Werner Koch's avatar
Werner Koch committed
594
      if (myopt->pass)
595
        log_info (_("          pass '%s'\n"), *myopt->pass?"*****":"");
Werner Koch's avatar
Werner Koch committed
596
      if (host)
597
        log_info (_("          host '%s'\n"), host);
Werner Koch's avatar
Werner Koch committed
598 599
      log_info (_("          port %d\n"), port);
      if (dn)
600
        log_info (_("            DN '%s'\n"), dn);
Werner Koch's avatar
Werner Koch committed
601
      if (filter)
602
        log_info (_("        filter '%s'\n"), filter);
Werner Koch's avatar
Werner Koch committed
603
      if (myopt->multi && !myopt->attr && ludp->lud_attrs)
Werner Koch's avatar
Werner Koch committed
604 605 606
        {
          int i;
          for (i=0; ludp->lud_attrs[i]; i++)
607
            log_info (_("          attr '%s'\n"), ludp->lud_attrs[i]);
Werner Koch's avatar
Werner Koch committed
608 609
        }
      else if (attr)
610
        log_info (_("          attr '%s'\n"), attr);
Werner Koch's avatar
Werner Koch committed
611 612 613 614 615
    }


  if (!host || !*host)
    {
616
      log_error (_("no host name in '%s'\n"), url);
Werner Koch's avatar
Werner Koch committed
617 618
      return -1;
    }
Werner Koch's avatar
Werner Koch committed
619
  if (!myopt->multi && !attr)
Werner Koch's avatar
Werner Koch committed
620
    {
621
      log_error (_("no attribute given for query '%s'\n"), url);
Werner Koch's avatar
Werner Koch committed
622 623 624
      return -1;
    }

Werner Koch's avatar
Werner Koch committed
625
  if (!myopt->multi && !myopt->attr
Werner Koch's avatar
Werner Koch committed
626 627 628 629
      && ludp->lud_attrs && ludp->lud_attrs[0] && ludp->lud_attrs[1])
    log_info (_("WARNING: using first attribute only\n"));


Werner Koch's avatar
Werner Koch committed
630
  set_timeout (myopt);
631
  npth_unprotect ();
Werner Koch's avatar
Werner Koch committed
632
  ld = my_ldap_init (host, port);
633
  npth_protect ();
Werner Koch's avatar
Werner Koch committed
634 635
  if (!ld)
    {
636
      log_error (_("LDAP init to '%s:%d' failed: %s\n"),
Werner Koch's avatar
Werner Koch committed
637 638 639
                 host, port, strerror (errno));
      return -1;
    }
640
  npth_unprotect ();
Werner Koch's avatar
Werner Koch committed
641 642
  /* Fixme:  Can we use MYOPT->user or is it shared with other theeads?.  */
  ret = my_ldap_simple_bind_s (ld, myopt->user, myopt->pass);
643
  npth_protect ();
644 645 646
#ifdef LDAP_VERSION3
  if (ret == LDAP_PROTOCOL_ERROR)
    {
647
      /* Protocol error could mean that the server only supports v3. */
648
      int version = LDAP_VERSION3;
649 650
      if (myopt->verbose)
        log_info ("protocol error; retrying bind with v3 protocol\n");
651 652 653 654 655 656
      npth_unprotect ();
      ldap_set_option (ld, LDAP_OPT_PROTOCOL_VERSION, &version);
      ret = my_ldap_simple_bind_s (ld, myopt->user, myopt->pass);
      npth_protect ();
    }
#endif
657
  if (ret)
Werner Koch's avatar
Werner Koch committed
658
    {
659
      log_error (_("binding to '%s:%d' failed: %s\n"),
660
                 host, port, ldap_err2string (ret));
Werner Koch's avatar
Werner Koch committed
661
      ldap_unbind (ld);
Werner Koch's avatar
Werner Koch committed
662 663 664
      return -1;
    }

Werner Koch's avatar
Werner Koch committed
665
  set_timeout (myopt);
666
  npth_unprotect ();
Werner Koch's avatar
Werner Koch committed
667 668 669 670 671
  rc = my_ldap_search_st (ld, dn, ludp->lud_scope, filter,
                          myopt->multi && !myopt->attr && ludp->lud_attrs?
                          ludp->lud_attrs:attrs,
                          0,
                          &myopt->timeout, &msg);
672
  npth_protect ();
Werner Koch's avatar
Werner Koch committed
673
  if (rc == LDAP_SIZELIMIT_EXCEEDED && myopt->multi)
Werner Koch's avatar
Werner Koch committed
674
    {
675
      if (es_fwrite ("E\0\0\0\x09truncated", 14, 1, myopt->outstream) != 1)
Werner Koch's avatar
Werner Koch committed
676
        {
Werner Koch's avatar
Werner Koch committed
677
          log_error (_("error writing to stdout: %s\n"), strerror (errno));
Werner Koch's avatar
Werner Koch committed
678 679 680 681 682
          return -1;
        }
    }
  else if (rc)
    {
Werner Koch's avatar
Werner Koch committed
683
#ifdef HAVE_W32CE_SYSTEM
684
      log_error ("searching '%s' failed: %d\n", url, rc);
Werner Koch's avatar
Werner Koch committed
685
#else
686
      log_error (_("searching '%s' failed: %s\n"),
Werner Koch's avatar
Werner Koch committed
687
                 url, ldap_err2string (rc));
Werner Koch's avatar
Werner Koch committed
688
#endif
Werner Koch's avatar
Werner Koch committed
689 690 691 692 693 694 695 696
      if (rc != LDAP_NO_SUCH_OBJECT)
        {
          /* FIXME: Need deinit (ld)?  */
          /* Hmmm: Do we need to released MSG in case of an error? */
          return -1;
        }
    }

Werner Koch's avatar
Werner Koch committed
697
  rc = print_ldap_entries (myopt, ld, msg, myopt->multi? NULL:attr);
Werner Koch's avatar
Werner Koch committed
698 699

  ldap_msgfree (msg);
Werner Koch's avatar
Werner Koch committed
700
  ldap_unbind (ld);
Werner Koch's avatar
Werner Koch committed
701 702 703 704 705 706 707 708 709
  return rc;
}




/* Main processing.  Take the URL and run the LDAP query. The result
   is printed to stdout, errors are logged to the log stream. */
static int
Werner Koch's avatar
Werner Koch committed
710
process_url (my_opt_t myopt, const char *url)
Werner Koch's avatar
Werner Koch committed
711 712 713 714 715 716 717
{
  int rc;
  LDAPURLDesc *ludp = NULL;


  if (!ldap_is_ldap_url (url))
    {
718
      log_error (_("'%s' is not an LDAP URL\n"), url);
Werner Koch's avatar
Werner Koch committed
719 720 721 722 723
      return -1;
    }

  if (ldap_url_parse (url, &ludp))
    {
724
      log_error (_("'%s' is an invalid LDAP URL\n"), url);
Werner Koch's avatar
Werner Koch committed
725 726 727
      return -1;
    }

Werner Koch's avatar
Werner Koch committed
728
  rc = fetch_ldap (myopt, url, ludp);
Werner Koch's avatar
Werner Koch committed
729 730 731 732

  ldap_free_urldesc (ludp);
  return rc;
}