[BACK]Return to ldapclient.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / ldap

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: }