Annotation of src/usr.bin/ldap/ldapclient.c, Revision 1.1
1.1 ! reyk 1: /* $OpenBSD$ */
! 2:
! 3: /*
! 4: * Copyright (c) 2018 Reyk Floeter <reyk@openbsd.org>
! 5: *
! 6: * Permission to use, copy, modify, and distribute this software for any
! 7: * purpose with or without fee is hereby granted, provided that the above
! 8: * copyright notice and this permission notice appear in all copies.
! 9: *
! 10: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
! 11: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
! 12: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
! 13: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
! 14: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
! 15: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
! 16: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
! 17: */
! 18:
! 19: #include <sys/param.h>
! 20: #include <sys/queue.h>
! 21: #include <sys/socket.h>
! 22: #include <sys/tree.h>
! 23: #include <sys/un.h>
! 24:
! 25: #include <netinet/in.h>
! 26: #include <arpa/inet.h>
! 27:
! 28: #include <stdio.h>
! 29: #include <stdlib.h>
! 30: #include <stdint.h>
! 31: #include <unistd.h>
! 32: #include <ctype.h>
! 33: #include <err.h>
! 34: #include <errno.h>
! 35: #include <event.h>
! 36: #include <fcntl.h>
! 37: #include <limits.h>
! 38: #include <netdb.h>
! 39: #include <pwd.h>
! 40: #include <readpassphrase.h>
! 41: #include <resolv.h>
! 42: #include <signal.h>
! 43: #include <string.h>
! 44: #include <vis.h>
! 45:
! 46: #include "aldap.h"
! 47: #include "log.h"
! 48:
! 49: #define F_STARTTLS 0x01
! 50: #define F_TLS 0x02
! 51: #define F_NEEDAUTH 0x04
! 52: #define F_LDIF 0x08
! 53:
! 54: #define CAPATH "/etc/ssl/cert.pem"
! 55: #define LDAPHOST "localhost"
! 56: #define LDAPFILTER "(objectClass=*)"
! 57: #define LDIF_LINELENGTH 79
! 58:
! 59: struct ldapc {
! 60: struct aldap *ldap_al;
! 61: char *ldap_host;
! 62: int ldap_port;
! 63: char *ldap_capath;
! 64: char *ldap_binddn;
! 65: char *ldap_secret;
! 66: unsigned int ldap_flags;
! 67: enum protocol_op ldap_req;
! 68: enum aldap_protocol ldap_protocol;
! 69: struct aldap_url ldap_url;
! 70: };
! 71:
! 72: struct ldapc_search {
! 73: int ls_sizelimit;
! 74: int ls_timelimit;
! 75: char *ls_basedn;
! 76: char *ls_filter;
! 77: int ls_scope;
! 78: char **ls_attr;
! 79: };
! 80:
! 81: __dead void usage(void);
! 82: int ldapc_connect(struct ldapc *);
! 83: int ldapc_search(struct ldapc *, struct ldapc_search *);
! 84: int ldapc_printattr(struct ldapc *, const char *, const char *);
! 85: void ldapc_disconnect(struct ldapc *);
! 86: int ldapc_parseurl(struct ldapc *, struct ldapc_search *,
! 87: const char *);
! 88: const char *ldapc_resultcode(enum result_code);
! 89: const char *url_decode(char *);
! 90:
! 91: __dead void
! 92: usage(void)
! 93: {
! 94: extern char *__progname;
! 95:
! 96: fprintf(stderr,
! 97: "usage: %s search [-LvxZ] [-b basedn] [-c capath] [-D binddn] [-H host]\n"
! 98: " [-l timelimit] [-s scope] [-w secret|-W] [-z sizelimit]\n"
! 99: " [filter] [attributes ...]\n",
! 100: __progname);
! 101:
! 102: exit(1);
! 103: }
! 104:
! 105: int
! 106: main(int argc, char *argv[])
! 107: {
! 108: char passbuf[BUFSIZ];
! 109: const char *errstr, *url = NULL;
! 110: struct ldapc ldap;
! 111: struct ldapc_search ls;
! 112: int ch;
! 113: int verbose = 1;
! 114:
! 115: if (pledge("stdio inet unix tty rpath dns", NULL) == -1)
! 116: err(1, "pledge");
! 117:
! 118: log_init(verbose, 0);
! 119:
! 120: memset(&ldap, 0, sizeof(ldap));
! 121: memset(&ls, 0, sizeof(ls));
! 122: ls.ls_scope = -1;
! 123: ldap.ldap_port = -1;
! 124:
! 125: /*
! 126: * Check the command. Currently only "search" is supported but
! 127: * it could be extended with others such as add, modify, or delete.
! 128: */
! 129: if (argc < 2)
! 130: usage();
! 131: else if (strcmp("search", argv[1]) == 0)
! 132: ldap.ldap_req = LDAP_REQ_SEARCH;
! 133: else
! 134: usage();
! 135: argc--;
! 136: argv++;
! 137:
! 138: while ((ch = getopt(argc, argv, "b:c:D:H:Ll:s:vWw:xZz:")) != -1) {
! 139: switch (ch) {
! 140: case 'b':
! 141: ls.ls_basedn = optarg;
! 142: break;
! 143: case 'c':
! 144: ldap.ldap_capath = optarg;
! 145: break;
! 146: case 'D':
! 147: ldap.ldap_binddn = optarg;
! 148: ldap.ldap_flags |= F_NEEDAUTH;
! 149: break;
! 150: case 'H':
! 151: url = optarg;
! 152: break;
! 153: case 'L':
! 154: ldap.ldap_flags |= F_LDIF;
! 155: break;
! 156: case 'l':
! 157: ls.ls_timelimit = strtonum(optarg, 0, INT_MAX,
! 158: &errstr);
! 159: if (errstr != NULL)
! 160: errx(1, "timelimit %s", errstr);
! 161: break;
! 162: case 's':
! 163: if (strcasecmp("base", optarg) == 0)
! 164: ls.ls_scope = LDAP_SCOPE_BASE;
! 165: else if (strcasecmp("one", optarg) == 0)
! 166: ls.ls_scope = LDAP_SCOPE_ONELEVEL;
! 167: else if (strcasecmp("sub", optarg) == 0)
! 168: ls.ls_scope = LDAP_SCOPE_SUBTREE;
! 169: else
! 170: errx(1, "invalid scope: %s", optarg);
! 171: break;
! 172: case 'v':
! 173: verbose++;
! 174: break;
! 175: case 'w':
! 176: ldap.ldap_secret = optarg;
! 177: ldap.ldap_flags |= F_NEEDAUTH;
! 178: break;
! 179: case 'W':
! 180: ldap.ldap_flags |= F_NEEDAUTH;
! 181: break;
! 182: case 'x':
! 183: /* provided for compatibility */
! 184: break;
! 185: case 'Z':
! 186: ldap.ldap_flags |= F_STARTTLS;
! 187: break;
! 188: case 'z':
! 189: ls.ls_sizelimit = strtonum(optarg, 0, INT_MAX,
! 190: &errstr);
! 191: if (errstr != NULL)
! 192: errx(1, "sizelimit %s", errstr);
! 193: break;
! 194: default:
! 195: usage();
! 196: }
! 197: }
! 198: argc -= optind;
! 199: argv += optind;
! 200:
! 201: log_setverbose(verbose);
! 202:
! 203: if (url != NULL && ldapc_parseurl(&ldap, &ls, url) == -1)
! 204: errx(1, "ldapurl");
! 205:
! 206: /* Set the default after parsing URL and/or options */
! 207: if (ldap.ldap_host == NULL)
! 208: ldap.ldap_host = LDAPHOST;
! 209: if (ldap.ldap_port == -1)
! 210: ldap.ldap_port = ldap.ldap_protocol == LDAPS ?
! 211: LDAPS_PORT : LDAP_PORT;
! 212: if (ldap.ldap_protocol == LDAP && (ldap.ldap_flags & F_STARTTLS))
! 213: ldap.ldap_protocol = LDAPTLS;
! 214: if (ldap.ldap_capath == NULL)
! 215: ldap.ldap_capath = CAPATH;
! 216: if (ls.ls_basedn == NULL)
! 217: ls.ls_basedn = "";
! 218: if (ls.ls_scope == -1)
! 219: ls.ls_scope = LDAP_SCOPE_SUBTREE;
! 220: if (ls.ls_filter == NULL)
! 221: ls.ls_filter = LDAPFILTER;
! 222:
! 223: if (ldap.ldap_flags & F_NEEDAUTH) {
! 224: if (ldap.ldap_binddn == NULL) {
! 225: log_warnx("missing -D binddn");
! 226: usage();
! 227: }
! 228: if (ldap.ldap_secret == NULL) {
! 229: if (readpassphrase("Password: ",
! 230: passbuf, sizeof(passbuf), RPP_REQUIRE_TTY) == NULL)
! 231: errx(1, "failed to read LDAP password");
! 232: ldap.ldap_secret = passbuf;
! 233: }
! 234: }
! 235:
! 236: if (pledge("stdio inet unix rpath dns", NULL) == -1)
! 237: err(1, "pledge");
! 238:
! 239: /* optional search filter */
! 240: if (argc && strchr(argv[0], '=') != NULL) {
! 241: ls.ls_filter = argv[0];
! 242: argc--;
! 243: argv++;
! 244: }
! 245: /* search attributes */
! 246: if (argc)
! 247: ls.ls_attr = argv;
! 248:
! 249: if (ldapc_connect(&ldap) == -1)
! 250: errx(1, "LDAP connection failed");
! 251:
! 252: if (pledge("stdio", NULL) == -1)
! 253: err(1, "pledge");
! 254:
! 255: if (ldapc_search(&ldap, &ls) == -1)
! 256: errx(1, "LDAP search failed");
! 257:
! 258: ldapc_disconnect(&ldap);
! 259: aldap_free_url(&ldap.ldap_url);
! 260:
! 261: return (0);
! 262: }
! 263:
! 264: int
! 265: ldapc_search(struct ldapc *ldap, struct ldapc_search *ls)
! 266: {
! 267: struct aldap_page_control *pg = NULL;
! 268: struct aldap_message *m;
! 269: const char *errstr;
! 270: const char *searchdn, *dn = NULL;
! 271: char *outkey;
! 272: char **outvalues;
! 273: int ret, i, code, fail = 0;
! 274:
! 275: do {
! 276: if (aldap_search(ldap->ldap_al, ls->ls_basedn, ls->ls_scope,
! 277: ls->ls_filter, ls->ls_attr, 0, ls->ls_sizelimit,
! 278: ls->ls_timelimit, pg) == -1) {
! 279: aldap_get_errno(ldap->ldap_al, &errstr);
! 280: log_warnx("LDAP search failed: %s", errstr);
! 281: return (-1);
! 282: }
! 283:
! 284: if (pg != NULL) {
! 285: aldap_freepage(pg);
! 286: pg = NULL;
! 287: }
! 288:
! 289: while ((m = aldap_parse(ldap->ldap_al)) != NULL) {
! 290: if (ldap->ldap_al->msgid != m->msgid) {
! 291: goto fail;
! 292: }
! 293:
! 294: if ((code = aldap_get_resultcode(m)) != LDAP_SUCCESS) {
! 295: log_warnx("LDAP search failed: %s(%d)",
! 296: ldapc_resultcode(code), code);
! 297: break;
! 298: }
! 299:
! 300: if (m->message_type == LDAP_RES_SEARCH_RESULT) {
! 301: if (m->page != NULL && m->page->cookie_len != 0)
! 302: pg = m->page;
! 303: else
! 304: pg = NULL;
! 305:
! 306: aldap_freemsg(m);
! 307: break;
! 308: }
! 309:
! 310: if (m->message_type != LDAP_RES_SEARCH_ENTRY) {
! 311: goto fail;
! 312: }
! 313:
! 314: if (aldap_count_attrs(m) < 1) {
! 315: aldap_freemsg(m);
! 316: continue;
! 317: }
! 318:
! 319: if ((searchdn = aldap_get_dn(m)) == NULL)
! 320: goto fail;
! 321:
! 322: if (dn != NULL)
! 323: printf("\n");
! 324: else
! 325: dn = ls->ls_basedn;
! 326: if (strcmp(dn, searchdn) != 0)
! 327: printf("dn: %s\n", searchdn);
! 328:
! 329: for (ret = aldap_first_attr(m, &outkey, &outvalues);
! 330: ret != -1;
! 331: ret = aldap_next_attr(m, &outkey, &outvalues)) {
! 332: for (i = 0; outvalues != NULL &&
! 333: outvalues[i] != NULL; i++) {
! 334: if (ldapc_printattr(ldap, outkey,
! 335: outvalues[i]) == -1) {
! 336: fail = 1;
! 337: break;
! 338: }
! 339: }
! 340: }
! 341: free(outkey);
! 342: aldap_free_attr(outvalues);
! 343:
! 344: aldap_freemsg(m);
! 345: }
! 346: } while (pg != NULL && fail == 0);
! 347:
! 348: if (fail)
! 349: return (-1);
! 350: return (0);
! 351: fail:
! 352: ldapc_disconnect(ldap);
! 353: return (-1);
! 354: }
! 355:
! 356: int
! 357: ldapc_printattr(struct ldapc *ldap, const char *key, const char *value)
! 358: {
! 359: char *p = NULL, *out;
! 360: const unsigned char *cp;
! 361: int encode;
! 362: size_t inlen, outlen, left;
! 363:
! 364: if (ldap->ldap_flags & F_LDIF) {
! 365: /* OpenLDAP encodes the userPassword by default */
! 366: if (strcasecmp("userPassword", key) == 0)
! 367: encode = 1;
! 368: else
! 369: encode = 0;
! 370:
! 371: /*
! 372: * The LDIF format a set of characters that can be included
! 373: * in SAFE-STRINGs. String value that do not match the
! 374: * criteria must be encoded as Base64.
! 375: */
! 376: for (cp = (const unsigned char *)value;
! 377: encode == 0 &&*cp != '\0'; cp++) {
! 378: /* !SAFE-CHAR %x01-09 / %x0B-0C / %x0E-7F */
! 379: if (*cp > 127 ||
! 380: *cp == '\0' ||
! 381: *cp == '\n' ||
! 382: *cp == '\r')
! 383: encode = 1;
! 384: }
! 385:
! 386: if (!encode) {
! 387: if (asprintf(&p, "%s: %s", key, value) == -1) {
! 388: log_warnx("asprintf");
! 389: return (-1);
! 390: }
! 391: } else {
! 392: inlen = strlen(value);
! 393: outlen = inlen * 2 + 1;
! 394:
! 395: if ((out = calloc(1, outlen)) == NULL ||
! 396: b64_ntop(value, inlen, out, outlen) == -1) {
! 397: log_warnx("Base64 encoding failed");
! 398: free(p);
! 399: return (-1);
! 400: }
! 401:
! 402: /* Base64 is indicated with a double-colon */
! 403: if (asprintf(&p, "%s: %s", key, out) == -1) {
! 404: log_warnx("asprintf");
! 405: free(out);
! 406: return (-1);
! 407: }
! 408: free(out);
! 409: }
! 410:
! 411: /* Wrap lines */
! 412: for (outlen = 0, inlen = strlen(p);
! 413: outlen < inlen;
! 414: outlen += LDIF_LINELENGTH) {
! 415: if (outlen)
! 416: putchar(' ');
! 417: /* max. line length - newline - optional indent */
! 418: left = MIN(inlen - outlen, outlen ?
! 419: LDIF_LINELENGTH - 2 :
! 420: LDIF_LINELENGTH - 1);
! 421: fwrite(p + outlen, left, 1, stdout);
! 422: putchar('\n');
! 423: }
! 424: } else {
! 425: /*
! 426: * Use vis(1) instead of base64 encoding of non-printable
! 427: * values. This is much nicer as it always prdocues a
! 428: * human-readable visual output. This can safely be done
! 429: * on all values no matter if they include non-printable
! 430: * characters.
! 431: */
! 432: if (stravis(&p, value, VIS_SAFE|VIS_NL) == -1) {
! 433: log_warn("visual encoding failed");
! 434: return (-1);
! 435: }
! 436:
! 437: printf("%s: %s\n", key, p);
! 438: }
! 439:
! 440: free(p);
! 441: return (0);
! 442: }
! 443:
! 444: int
! 445: ldapc_connect(struct ldapc *ldap)
! 446: {
! 447: struct addrinfo ai, *res, *res0;
! 448: struct sockaddr_un un;
! 449: int ret = -1, saved_errno, fd = -1, code;
! 450: struct aldap_message *m;
! 451: const char *errstr;
! 452: struct tls_config *tls_config;
! 453: char port[6];
! 454:
! 455: if (ldap->ldap_protocol == LDAPI) {
! 456: memset(&un, 0, sizeof(un));
! 457: un.sun_family = AF_UNIX;
! 458: if (strlcpy(un.sun_path, ldap->ldap_host,
! 459: sizeof(un.sun_path)) >= sizeof(un.sun_path)) {
! 460: log_warnx("socket '%s' too long", ldap->ldap_host);
! 461: goto done;
! 462: }
! 463: if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1 ||
! 464: connect(fd, (struct sockaddr *)&un, sizeof(un)) == -1)
! 465: goto done;
! 466: goto init;
! 467: }
! 468:
! 469: memset(&ai, 0, sizeof(ai));
! 470: ai.ai_family = AF_UNSPEC;
! 471: ai.ai_socktype = SOCK_STREAM;
! 472: ai.ai_protocol = IPPROTO_TCP;
! 473: (void)snprintf(port, sizeof(port), "%u", ldap->ldap_port);
! 474: if ((code = getaddrinfo(ldap->ldap_host, port,
! 475: &ai, &res0)) != 0) {
! 476: log_warnx("%s", gai_strerror(code));
! 477: goto done;
! 478: }
! 479: for (res = res0; res; res = res->ai_next, fd = -1) {
! 480: if ((fd = socket(res->ai_family, res->ai_socktype,
! 481: res->ai_protocol)) == -1)
! 482: continue;
! 483:
! 484: if (connect(fd, res->ai_addr, res->ai_addrlen) >= 0)
! 485: break;
! 486:
! 487: saved_errno = errno;
! 488: close(fd);
! 489: errno = saved_errno;
! 490: }
! 491: freeaddrinfo(res0);
! 492: if (fd == -1)
! 493: goto done;
! 494:
! 495: init:
! 496: if ((ldap->ldap_al = aldap_init(fd)) == NULL) {
! 497: warn("LDAP init failed");
! 498: close(fd);
! 499: goto done;
! 500: }
! 501:
! 502: if (ldap->ldap_flags & F_STARTTLS) {
! 503: log_debug("%s: requesting STARTTLS", __func__);
! 504: if (aldap_req_starttls(ldap->ldap_al) == -1) {
! 505: log_warnx("failed to request STARTTLS");
! 506: goto done;
! 507: }
! 508:
! 509: if ((m = aldap_parse(ldap->ldap_al)) == NULL) {
! 510: log_warnx("failed to parse STARTTLS response");
! 511: goto done;
! 512: }
! 513:
! 514: if (ldap->ldap_al->msgid != m->msgid ||
! 515: (code = aldap_get_resultcode(m)) != LDAP_SUCCESS) {
! 516: log_warnx("STARTTLS failed: %s(%d)",
! 517: ldapc_resultcode(code), code);
! 518: aldap_freemsg(m);
! 519: goto done;
! 520: }
! 521: aldap_freemsg(m);
! 522: }
! 523:
! 524: if (ldap->ldap_flags & (F_STARTTLS | F_TLS)) {
! 525: log_debug("%s: starting TLS", __func__);
! 526:
! 527: if ((tls_config = tls_config_new()) == NULL) {
! 528: log_warnx("TLS config failed");
! 529: goto done;
! 530: }
! 531:
! 532: if (tls_config_set_ca_file(tls_config,
! 533: ldap->ldap_capath) == -1) {
! 534: log_warnx("unable to set CA %s", ldap->ldap_capath);
! 535: goto done;
! 536: }
! 537:
! 538: if (aldap_tls(ldap->ldap_al, tls_config, ldap->ldap_host) < 0) {
! 539: aldap_get_errno(ldap->ldap_al, &errstr);
! 540: log_warnx("TLS failed: %s", errstr);
! 541: goto done;
! 542: }
! 543: }
! 544:
! 545: if (ldap->ldap_flags & F_NEEDAUTH) {
! 546: log_debug("%s: bind request", __func__);
! 547: if (aldap_bind(ldap->ldap_al, ldap->ldap_binddn,
! 548: ldap->ldap_secret) == -1) {
! 549: log_warnx("bind request failed");
! 550: goto done;
! 551: }
! 552:
! 553: if ((m = aldap_parse(ldap->ldap_al)) == NULL) {
! 554: log_warnx("failed to parse bind response");
! 555: goto done;
! 556: }
! 557:
! 558: if (ldap->ldap_al->msgid != m->msgid ||
! 559: (code = aldap_get_resultcode(m)) != LDAP_SUCCESS) {
! 560: log_warnx("bind failed: %s(%d)",
! 561: ldapc_resultcode(code), code);
! 562: aldap_freemsg(m);
! 563: goto done;
! 564: }
! 565: aldap_freemsg(m);
! 566: }
! 567:
! 568: log_debug("%s: connected", __func__);
! 569:
! 570: ret = 0;
! 571: done:
! 572: if (ret != 0)
! 573: ldapc_disconnect(ldap);
! 574: if (ldap->ldap_secret != NULL)
! 575: explicit_bzero(ldap->ldap_secret,
! 576: strlen(ldap->ldap_secret));
! 577: return (ret);
! 578: }
! 579:
! 580: void
! 581: ldapc_disconnect(struct ldapc *ldap)
! 582: {
! 583: if (ldap->ldap_al == NULL)
! 584: return;
! 585: aldap_close(ldap->ldap_al);
! 586: ldap->ldap_al = NULL;
! 587: }
! 588:
! 589: const char *
! 590: ldapc_resultcode(enum result_code code)
! 591: {
! 592: #define CODE(_X) case _X:return (#_X)
! 593: switch (code) {
! 594: CODE(LDAP_SUCCESS);
! 595: CODE(LDAP_OPERATIONS_ERROR);
! 596: CODE(LDAP_PROTOCOL_ERROR);
! 597: CODE(LDAP_TIMELIMIT_EXCEEDED);
! 598: CODE(LDAP_SIZELIMIT_EXCEEDED);
! 599: CODE(LDAP_COMPARE_FALSE);
! 600: CODE(LDAP_COMPARE_TRUE);
! 601: CODE(LDAP_STRONG_AUTH_NOT_SUPPORTED);
! 602: CODE(LDAP_STRONG_AUTH_REQUIRED);
! 603: CODE(LDAP_REFERRAL);
! 604: CODE(LDAP_ADMINLIMIT_EXCEEDED);
! 605: CODE(LDAP_UNAVAILABLE_CRITICAL_EXTENSION);
! 606: CODE(LDAP_CONFIDENTIALITY_REQUIRED);
! 607: CODE(LDAP_SASL_BIND_IN_PROGRESS);
! 608: CODE(LDAP_NO_SUCH_ATTRIBUTE);
! 609: CODE(LDAP_UNDEFINED_TYPE);
! 610: CODE(LDAP_INAPPROPRIATE_MATCHING);
! 611: CODE(LDAP_CONSTRAINT_VIOLATION);
! 612: CODE(LDAP_TYPE_OR_VALUE_EXISTS);
! 613: CODE(LDAP_INVALID_SYNTAX);
! 614: CODE(LDAP_NO_SUCH_OBJECT);
! 615: CODE(LDAP_ALIAS_PROBLEM);
! 616: CODE(LDAP_INVALID_DN_SYNTAX);
! 617: CODE(LDAP_ALIAS_DEREF_PROBLEM);
! 618: CODE(LDAP_INAPPROPRIATE_AUTH);
! 619: CODE(LDAP_INVALID_CREDENTIALS);
! 620: CODE(LDAP_INSUFFICIENT_ACCESS);
! 621: CODE(LDAP_BUSY);
! 622: CODE(LDAP_UNAVAILABLE);
! 623: CODE(LDAP_UNWILLING_TO_PERFORM);
! 624: CODE(LDAP_LOOP_DETECT);
! 625: CODE(LDAP_NAMING_VIOLATION);
! 626: CODE(LDAP_OBJECT_CLASS_VIOLATION);
! 627: CODE(LDAP_NOT_ALLOWED_ON_NONLEAF);
! 628: CODE(LDAP_NOT_ALLOWED_ON_RDN);
! 629: CODE(LDAP_ALREADY_EXISTS);
! 630: CODE(LDAP_NO_OBJECT_CLASS_MODS);
! 631: CODE(LDAP_AFFECTS_MULTIPLE_DSAS);
! 632: CODE(LDAP_OTHER);
! 633: default:
! 634: return ("UNKNOWN_ERROR");
! 635: }
! 636: };
! 637:
! 638: int
! 639: ldapc_parseurl(struct ldapc *ldap, struct ldapc_search *ls, const char *url)
! 640: {
! 641: struct aldap_url *lu = &ldap->ldap_url;
! 642: size_t i;
! 643:
! 644: memset(lu, 0, sizeof(*lu));
! 645: lu->scope = -1;
! 646:
! 647: if (aldap_parse_url(url, lu) == -1) {
! 648: log_warnx("failed to parse LDAP URL");
! 649: return (-1);
! 650: }
! 651:
! 652: /* The protocol part is optional and we default to ldap:// */
! 653: if (lu->protocol == -1)
! 654: lu->protocol = LDAP;
! 655: else if (lu->protocol == LDAPI) {
! 656: if (lu->port != 0 ||
! 657: url_decode(lu->host) == NULL) {
! 658: log_warnx("invalid ldapi:// URL");
! 659: return (-1);
! 660: }
! 661: } else if ((ldap->ldap_flags & F_STARTTLS) &&
! 662: lu->protocol != LDAPTLS) {
! 663: log_warnx("conflicting protocol arguments");
! 664: return (-1);
! 665: } else if (lu->protocol == LDAPTLS)
! 666: ldap->ldap_flags |= F_TLS|F_STARTTLS;
! 667: else if (lu->protocol == LDAPS)
! 668: ldap->ldap_flags |= F_TLS;
! 669: ldap->ldap_protocol = lu->protocol;
! 670:
! 671: ldap->ldap_host = lu->host;
! 672: if (lu->port)
! 673: ldap->ldap_port = lu->port;
! 674:
! 675: /* The distinguished name has to be URL-encoded */
! 676: if (lu->dn != NULL && ls->ls_basedn != NULL &&
! 677: strcasecmp(ls->ls_basedn, lu->dn) != 0) {
! 678: log_warnx("conflicting basedn arguments");
! 679: return (-1);
! 680: }
! 681: if (lu->dn != NULL) {
! 682: if (url_decode(lu->dn) == NULL)
! 683: return (-1);
! 684: ls->ls_basedn = lu->dn;
! 685: }
! 686:
! 687: if (lu->scope != -1) {
! 688: if (ls->ls_scope != -1 && (ls->ls_scope != lu->scope)) {
! 689: log_warnx("conflicting scope arguments");
! 690: return (-1);
! 691: }
! 692: ls->ls_scope = lu->scope;
! 693: }
! 694:
! 695: /* URL-decode optional attributes and the search filter */
! 696: if (lu->attributes[0] != NULL) {
! 697: for (i = 0; i < MAXATTR && lu->attributes[i] != NULL; i++)
! 698: if (url_decode(lu->attributes[i]) == NULL)
! 699: return (-1);
! 700: ls->ls_attr = lu->attributes;
! 701: }
! 702: if (lu->filter != NULL) {
! 703: if (url_decode(lu->filter) == NULL)
! 704: return (-1);
! 705: ls->ls_filter = lu->filter;
! 706: }
! 707:
! 708: return (0);
! 709: }
! 710:
! 711: /* From usr.sbin/httpd/httpd.c */
! 712: const char *
! 713: url_decode(char *url)
! 714: {
! 715: char *p, *q;
! 716: char hex[3];
! 717: unsigned long x;
! 718:
! 719: hex[2] = '\0';
! 720: p = q = url;
! 721:
! 722: while (*p != '\0') {
! 723: switch (*p) {
! 724: case '%':
! 725: /* Encoding character is followed by two hex chars */
! 726: if (!(isxdigit((unsigned char)p[1]) &&
! 727: isxdigit((unsigned char)p[2])))
! 728: return (NULL);
! 729:
! 730: hex[0] = p[1];
! 731: hex[1] = p[2];
! 732:
! 733: /*
! 734: * We don't have to validate "hex" because it is
! 735: * guaranteed to include two hex chars followed by nul.
! 736: */
! 737: x = strtoul(hex, NULL, 16);
! 738: *q = (char)x;
! 739: p += 2;
! 740: break;
! 741: default:
! 742: *q = *p;
! 743: break;
! 744: }
! 745: p++;
! 746: q++;
! 747: }
! 748: *q = '\0';
! 749:
! 750: return (url);
! 751: }