[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.4

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