[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

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