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

1.12    ! deraadt     1: /*     $OpenBSD: ldapclient.c,v 1.11 2018/11/29 14:25:07 tedu 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 LDAPHOST       "localhost"
                     56: #define LDAPFILTER     "(objectClass=*)"
                     57: #define LDIF_LINELENGTH        79
1.2       reyk       58: #define LDAPPASSMAX    1024
1.1       reyk       59:
                     60: struct ldapc {
                     61:        struct aldap            *ldap_al;
                     62:        char                    *ldap_host;
                     63:        int                      ldap_port;
1.11      tedu       64:        const char              *ldap_capath;
1.1       reyk       65:        char                    *ldap_binddn;
                     66:        char                    *ldap_secret;
                     67:        unsigned int             ldap_flags;
                     68:        enum protocol_op         ldap_req;
                     69:        enum aldap_protocol      ldap_protocol;
                     70:        struct aldap_url         ldap_url;
                     71: };
                     72:
                     73: struct ldapc_search {
                     74:        int                      ls_sizelimit;
                     75:        int                      ls_timelimit;
                     76:        char                    *ls_basedn;
                     77:        char                    *ls_filter;
                     78:        int                      ls_scope;
                     79:        char                    **ls_attr;
                     80: };
                     81:
                     82: __dead void     usage(void);
                     83: int             ldapc_connect(struct ldapc *);
                     84: int             ldapc_search(struct ldapc *, struct ldapc_search *);
1.10      martijn    85: int             ldapc_printattr(struct ldapc *, const char *,
                     86:                    const struct ber_octetstring *);
1.1       reyk       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)
1.11      tedu      223:                ldap.ldap_capath = tls_default_ca_cert_file();
1.1       reyk      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;
1.10      martijn   301:        struct aldap_stringset          *outvalues;
                    302:        int                              ret, code, fail = 0;
                    303:        size_t                           i;
1.1       reyk      304:
1.5       martijn   305:        if (ldap->ldap_flags & F_LDIF)
                    306:                printf("version: 1\n");
1.1       reyk      307:        do {
                    308:                if (aldap_search(ldap->ldap_al, ls->ls_basedn, ls->ls_scope,
                    309:                    ls->ls_filter, ls->ls_attr, 0, ls->ls_sizelimit,
                    310:                    ls->ls_timelimit, pg) == -1) {
                    311:                        aldap_get_errno(ldap->ldap_al, &errstr);
                    312:                        log_warnx("LDAP search failed: %s", errstr);
                    313:                        return (-1);
                    314:                }
                    315:
                    316:                if (pg != NULL) {
                    317:                        aldap_freepage(pg);
                    318:                        pg = NULL;
                    319:                }
                    320:
                    321:                while ((m = aldap_parse(ldap->ldap_al)) != NULL) {
                    322:                        if (ldap->ldap_al->msgid != m->msgid) {
                    323:                                goto fail;
                    324:                        }
                    325:
                    326:                        if ((code = aldap_get_resultcode(m)) != LDAP_SUCCESS) {
                    327:                                log_warnx("LDAP search failed: %s(%d)",
                    328:                                    ldapc_resultcode(code), code);
                    329:                                break;
                    330:                        }
                    331:
                    332:                        if (m->message_type == LDAP_RES_SEARCH_RESULT) {
                    333:                                if (m->page != NULL && m->page->cookie_len != 0)
                    334:                                        pg = m->page;
                    335:                                else
                    336:                                        pg = NULL;
                    337:
                    338:                                aldap_freemsg(m);
                    339:                                break;
                    340:                        }
                    341:
                    342:                        if (m->message_type != LDAP_RES_SEARCH_ENTRY) {
                    343:                                goto fail;
                    344:                        }
                    345:
                    346:                        if (aldap_count_attrs(m) < 1) {
                    347:                                aldap_freemsg(m);
                    348:                                continue;
                    349:                        }
                    350:
                    351:                        if ((searchdn = aldap_get_dn(m)) == NULL)
                    352:                                goto fail;
                    353:
                    354:                        if (dn != NULL)
                    355:                                printf("\n");
                    356:                        else
                    357:                                dn = ls->ls_basedn;
                    358:                        if (strcmp(dn, searchdn) != 0)
                    359:                                printf("dn: %s\n", searchdn);
                    360:
                    361:                        for (ret = aldap_first_attr(m, &outkey, &outvalues);
                    362:                            ret != -1;
                    363:                            ret = aldap_next_attr(m, &outkey, &outvalues)) {
1.10      martijn   364:                                for (i = 0; i < outvalues->len; i++) {
1.1       reyk      365:                                        if (ldapc_printattr(ldap, outkey,
1.10      martijn   366:                                            &(outvalues->str[i])) == -1) {
1.1       reyk      367:                                                fail = 1;
                    368:                                                break;
                    369:                                        }
                    370:                                }
                    371:                        }
                    372:                        free(outkey);
                    373:                        aldap_free_attr(outvalues);
                    374:
                    375:                        aldap_freemsg(m);
                    376:                }
                    377:        } while (pg != NULL && fail == 0);
                    378:
                    379:        if (fail)
                    380:                return (-1);
                    381:        return (0);
                    382:  fail:
                    383:        ldapc_disconnect(ldap);
                    384:        return (-1);
                    385: }
                    386:
                    387: int
1.10      martijn   388: ldapc_printattr(struct ldapc *ldap, const char *key,
                    389:     const struct ber_octetstring *value)
1.1       reyk      390: {
                    391:        char                    *p = NULL, *out;
                    392:        const unsigned char     *cp;
                    393:        int                      encode;
1.10      martijn   394:        size_t                   i, inlen, outlen, left;
1.1       reyk      395:
                    396:        if (ldap->ldap_flags & F_LDIF) {
                    397:                /* OpenLDAP encodes the userPassword by default */
                    398:                if (strcasecmp("userPassword", key) == 0)
                    399:                        encode = 1;
                    400:                else
                    401:                        encode = 0;
                    402:
                    403:                /*
                    404:                 * The LDIF format a set of characters that can be included
                    405:                 * in SAFE-STRINGs. String value that do not match the
                    406:                 * criteria must be encoded as Base64.
                    407:                 */
1.10      martijn   408:                cp = (const unsigned char *)value->ostr_val;
1.6       martijn   409:                /* !SAFE-INIT-CHAR: SAFE-CHAR minus %x20 %x3A %x3C */
                    410:                if (*cp == ' ' ||
                    411:                    *cp == ':' ||
                    412:                    *cp == '<')
                    413:                        encode = 1;
1.10      martijn   414:                for (i = 0; encode == 0 && i < value->ostr_len - 1; i++) {
1.1       reyk      415:                        /* !SAFE-CHAR %x01-09 / %x0B-0C / %x0E-7F */
1.10      martijn   416:                        if (cp[i] > 127 ||
                    417:                            cp[i] == '\0' ||
                    418:                            cp[i] == '\n' ||
                    419:                            cp[i] == '\r')
1.1       reyk      420:                                encode = 1;
                    421:                }
                    422:
                    423:                if (!encode) {
1.10      martijn   424:                        if (asprintf(&p, "%s: %s", key,
                    425:                            (const char *)value->ostr_val) == -1) {
1.1       reyk      426:                                log_warnx("asprintf");
                    427:                                return (-1);
                    428:                        }
                    429:                } else {
1.10      martijn   430:                        outlen = (((value->ostr_len + 2) / 3) * 4) + 1;
1.1       reyk      431:
                    432:                        if ((out = calloc(1, outlen)) == NULL ||
1.10      martijn   433:                            b64_ntop(value->ostr_val, value->ostr_len, out,
                    434:                            outlen) == -1) {
1.1       reyk      435:                                log_warnx("Base64 encoding failed");
                    436:                                free(p);
                    437:                                return (-1);
                    438:                        }
                    439:
                    440:                        /* Base64 is indicated with a double-colon */
1.9       martijn   441:                        if (asprintf(&p, "%s:: %s", key, out) == -1) {
1.1       reyk      442:                                log_warnx("asprintf");
                    443:                                free(out);
                    444:                                return (-1);
                    445:                        }
                    446:                        free(out);
                    447:                }
                    448:
                    449:                /* Wrap lines */
                    450:                for (outlen = 0, inlen = strlen(p);
                    451:                    outlen < inlen;
1.4       martijn   452:                    outlen += LDIF_LINELENGTH - 1) {
1.1       reyk      453:                        if (outlen)
                    454:                                putchar(' ');
1.4       martijn   455:                        if (outlen > LDIF_LINELENGTH)
                    456:                                outlen--;
1.1       reyk      457:                        /* max. line length - newline - optional indent */
                    458:                        left = MIN(inlen - outlen, outlen ?
                    459:                            LDIF_LINELENGTH - 2 :
                    460:                            LDIF_LINELENGTH - 1);
                    461:                        fwrite(p + outlen, left, 1, stdout);
                    462:                        putchar('\n');
                    463:                }
                    464:        } else {
                    465:                /*
                    466:                 * Use vis(1) instead of base64 encoding of non-printable
                    467:                 * values.  This is much nicer as it always prdocues a
                    468:                 * human-readable visual output.  This can safely be done
                    469:                 * on all values no matter if they include non-printable
                    470:                 * characters.
                    471:                 */
1.10      martijn   472:                p = calloc(1, 4 * value->ostr_len + 1);
                    473:                if (strvisx(p, value->ostr_val, value->ostr_len,
                    474:                    VIS_SAFE|VIS_NL) == -1) {
1.1       reyk      475:                        log_warn("visual encoding failed");
                    476:                        return (-1);
                    477:                }
                    478:
                    479:                printf("%s: %s\n", key, p);
                    480:        }
                    481:
                    482:        free(p);
                    483:        return (0);
                    484: }
                    485:
                    486: int
                    487: ldapc_connect(struct ldapc *ldap)
                    488: {
                    489:        struct addrinfo          ai, *res, *res0;
                    490:        struct sockaddr_un       un;
                    491:        int                      ret = -1, saved_errno, fd = -1, code;
                    492:        struct aldap_message    *m;
                    493:        const char              *errstr;
                    494:        struct tls_config       *tls_config;
                    495:        char                     port[6];
                    496:
                    497:        if (ldap->ldap_protocol == LDAPI) {
                    498:                memset(&un, 0, sizeof(un));
                    499:                un.sun_family = AF_UNIX;
                    500:                if (strlcpy(un.sun_path, ldap->ldap_host,
                    501:                    sizeof(un.sun_path)) >= sizeof(un.sun_path)) {
                    502:                        log_warnx("socket '%s' too long", ldap->ldap_host);
                    503:                        goto done;
                    504:                }
                    505:                if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1 ||
                    506:                    connect(fd, (struct sockaddr *)&un, sizeof(un)) == -1)
                    507:                        goto done;
                    508:                goto init;
                    509:        }
                    510:
                    511:        memset(&ai, 0, sizeof(ai));
                    512:        ai.ai_family = AF_UNSPEC;
                    513:        ai.ai_socktype = SOCK_STREAM;
                    514:        ai.ai_protocol = IPPROTO_TCP;
                    515:        (void)snprintf(port, sizeof(port), "%u", ldap->ldap_port);
                    516:        if ((code = getaddrinfo(ldap->ldap_host, port,
                    517:            &ai, &res0)) != 0) {
                    518:                log_warnx("%s", gai_strerror(code));
                    519:                goto done;
                    520:        }
                    521:        for (res = res0; res; res = res->ai_next, fd = -1) {
                    522:                if ((fd = socket(res->ai_family, res->ai_socktype,
                    523:                    res->ai_protocol)) == -1)
                    524:                        continue;
                    525:
                    526:                if (connect(fd, res->ai_addr, res->ai_addrlen) >= 0)
                    527:                        break;
                    528:
                    529:                saved_errno = errno;
                    530:                close(fd);
                    531:                errno = saved_errno;
                    532:        }
                    533:        freeaddrinfo(res0);
                    534:        if (fd == -1)
                    535:                goto done;
                    536:
                    537:  init:
                    538:        if ((ldap->ldap_al = aldap_init(fd)) == NULL) {
                    539:                warn("LDAP init failed");
                    540:                close(fd);
                    541:                goto done;
                    542:        }
                    543:
                    544:        if (ldap->ldap_flags & F_STARTTLS) {
                    545:                log_debug("%s: requesting STARTTLS", __func__);
                    546:                if (aldap_req_starttls(ldap->ldap_al) == -1) {
                    547:                        log_warnx("failed to request STARTTLS");
                    548:                        goto done;
                    549:                }
                    550:
                    551:                if ((m = aldap_parse(ldap->ldap_al)) == NULL) {
                    552:                        log_warnx("failed to parse STARTTLS response");
                    553:                        goto done;
                    554:                }
                    555:
                    556:                if (ldap->ldap_al->msgid != m->msgid ||
                    557:                    (code = aldap_get_resultcode(m)) != LDAP_SUCCESS) {
                    558:                        log_warnx("STARTTLS failed: %s(%d)",
                    559:                            ldapc_resultcode(code), code);
                    560:                        aldap_freemsg(m);
                    561:                        goto done;
                    562:                }
                    563:                aldap_freemsg(m);
                    564:        }
                    565:
                    566:        if (ldap->ldap_flags & (F_STARTTLS | F_TLS)) {
                    567:                log_debug("%s: starting TLS", __func__);
                    568:
                    569:                if ((tls_config = tls_config_new()) == NULL) {
                    570:                        log_warnx("TLS config failed");
                    571:                        goto done;
                    572:                }
                    573:
                    574:                if (tls_config_set_ca_file(tls_config,
                    575:                    ldap->ldap_capath) == -1) {
                    576:                        log_warnx("unable to set CA %s", ldap->ldap_capath);
                    577:                        goto done;
                    578:                }
                    579:
                    580:                if (aldap_tls(ldap->ldap_al, tls_config, ldap->ldap_host) < 0) {
                    581:                        aldap_get_errno(ldap->ldap_al, &errstr);
                    582:                        log_warnx("TLS failed: %s", errstr);
                    583:                        goto done;
                    584:                }
                    585:        }
                    586:
                    587:        if (ldap->ldap_flags & F_NEEDAUTH) {
                    588:                log_debug("%s: bind request", __func__);
                    589:                if (aldap_bind(ldap->ldap_al, ldap->ldap_binddn,
                    590:                    ldap->ldap_secret) == -1) {
                    591:                        log_warnx("bind request failed");
                    592:                        goto done;
                    593:                }
                    594:
                    595:                if ((m = aldap_parse(ldap->ldap_al)) == NULL) {
                    596:                        log_warnx("failed to parse bind response");
                    597:                        goto done;
                    598:                }
                    599:
                    600:                if (ldap->ldap_al->msgid != m->msgid ||
                    601:                    (code = aldap_get_resultcode(m)) != LDAP_SUCCESS) {
                    602:                        log_warnx("bind failed: %s(%d)",
                    603:                            ldapc_resultcode(code), code);
                    604:                        aldap_freemsg(m);
                    605:                        goto done;
                    606:                }
                    607:                aldap_freemsg(m);
                    608:        }
                    609:
                    610:        log_debug("%s: connected", __func__);
                    611:
                    612:        ret = 0;
                    613:  done:
                    614:        if (ret != 0)
                    615:                ldapc_disconnect(ldap);
                    616:        if (ldap->ldap_secret != NULL)
                    617:                explicit_bzero(ldap->ldap_secret,
                    618:                    strlen(ldap->ldap_secret));
                    619:        return (ret);
                    620: }
                    621:
                    622: void
                    623: ldapc_disconnect(struct ldapc *ldap)
                    624: {
                    625:        if (ldap->ldap_al == NULL)
                    626:                return;
                    627:        aldap_close(ldap->ldap_al);
                    628:        ldap->ldap_al = NULL;
                    629: }
                    630:
                    631: const char *
                    632: ldapc_resultcode(enum result_code code)
                    633: {
                    634: #define CODE(_X)       case _X:return (#_X)
                    635:        switch (code) {
                    636:        CODE(LDAP_SUCCESS);
                    637:        CODE(LDAP_OPERATIONS_ERROR);
                    638:        CODE(LDAP_PROTOCOL_ERROR);
                    639:        CODE(LDAP_TIMELIMIT_EXCEEDED);
                    640:        CODE(LDAP_SIZELIMIT_EXCEEDED);
                    641:        CODE(LDAP_COMPARE_FALSE);
                    642:        CODE(LDAP_COMPARE_TRUE);
                    643:        CODE(LDAP_STRONG_AUTH_NOT_SUPPORTED);
                    644:        CODE(LDAP_STRONG_AUTH_REQUIRED);
                    645:        CODE(LDAP_REFERRAL);
                    646:        CODE(LDAP_ADMINLIMIT_EXCEEDED);
                    647:        CODE(LDAP_UNAVAILABLE_CRITICAL_EXTENSION);
                    648:        CODE(LDAP_CONFIDENTIALITY_REQUIRED);
                    649:        CODE(LDAP_SASL_BIND_IN_PROGRESS);
                    650:        CODE(LDAP_NO_SUCH_ATTRIBUTE);
                    651:        CODE(LDAP_UNDEFINED_TYPE);
                    652:        CODE(LDAP_INAPPROPRIATE_MATCHING);
                    653:        CODE(LDAP_CONSTRAINT_VIOLATION);
                    654:        CODE(LDAP_TYPE_OR_VALUE_EXISTS);
                    655:        CODE(LDAP_INVALID_SYNTAX);
                    656:        CODE(LDAP_NO_SUCH_OBJECT);
                    657:        CODE(LDAP_ALIAS_PROBLEM);
                    658:        CODE(LDAP_INVALID_DN_SYNTAX);
                    659:        CODE(LDAP_ALIAS_DEREF_PROBLEM);
                    660:        CODE(LDAP_INAPPROPRIATE_AUTH);
                    661:        CODE(LDAP_INVALID_CREDENTIALS);
                    662:        CODE(LDAP_INSUFFICIENT_ACCESS);
                    663:        CODE(LDAP_BUSY);
                    664:        CODE(LDAP_UNAVAILABLE);
                    665:        CODE(LDAP_UNWILLING_TO_PERFORM);
                    666:        CODE(LDAP_LOOP_DETECT);
                    667:        CODE(LDAP_NAMING_VIOLATION);
                    668:        CODE(LDAP_OBJECT_CLASS_VIOLATION);
                    669:        CODE(LDAP_NOT_ALLOWED_ON_NONLEAF);
                    670:        CODE(LDAP_NOT_ALLOWED_ON_RDN);
                    671:        CODE(LDAP_ALREADY_EXISTS);
                    672:        CODE(LDAP_NO_OBJECT_CLASS_MODS);
                    673:        CODE(LDAP_AFFECTS_MULTIPLE_DSAS);
                    674:        CODE(LDAP_OTHER);
                    675:        default:
                    676:                return ("UNKNOWN_ERROR");
                    677:        }
                    678: };
                    679:
                    680: int
                    681: ldapc_parseurl(struct ldapc *ldap, struct ldapc_search *ls, const char *url)
                    682: {
                    683:        struct aldap_url        *lu = &ldap->ldap_url;
                    684:        size_t                   i;
                    685:
                    686:        memset(lu, 0, sizeof(*lu));
                    687:        lu->scope = -1;
                    688:
                    689:        if (aldap_parse_url(url, lu) == -1) {
                    690:                log_warnx("failed to parse LDAP URL");
                    691:                return (-1);
                    692:        }
                    693:
                    694:        /* The protocol part is optional and we default to ldap:// */
                    695:        if (lu->protocol == -1)
                    696:                lu->protocol = LDAP;
                    697:        else if (lu->protocol == LDAPI) {
                    698:                if (lu->port != 0 ||
                    699:                    url_decode(lu->host) == NULL) {
                    700:                        log_warnx("invalid ldapi:// URL");
                    701:                        return (-1);
                    702:                }
                    703:        } else if ((ldap->ldap_flags & F_STARTTLS) &&
                    704:            lu->protocol != LDAPTLS) {
                    705:                log_warnx("conflicting protocol arguments");
                    706:                return (-1);
                    707:        } else if (lu->protocol == LDAPTLS)
                    708:                ldap->ldap_flags |= F_TLS|F_STARTTLS;
                    709:        else if (lu->protocol == LDAPS)
                    710:                ldap->ldap_flags |= F_TLS;
                    711:        ldap->ldap_protocol = lu->protocol;
                    712:
                    713:        ldap->ldap_host = lu->host;
                    714:        if (lu->port)
                    715:                ldap->ldap_port = lu->port;
                    716:
                    717:        /* The distinguished name has to be URL-encoded */
                    718:        if (lu->dn != NULL && ls->ls_basedn != NULL &&
                    719:            strcasecmp(ls->ls_basedn, lu->dn) != 0) {
                    720:                log_warnx("conflicting basedn arguments");
                    721:                return (-1);
                    722:        }
                    723:        if (lu->dn != NULL) {
                    724:                if (url_decode(lu->dn) == NULL)
                    725:                        return (-1);
                    726:                ls->ls_basedn = lu->dn;
                    727:        }
                    728:
                    729:        if (lu->scope != -1) {
                    730:                if (ls->ls_scope != -1 && (ls->ls_scope != lu->scope)) {
                    731:                        log_warnx("conflicting scope arguments");
                    732:                        return (-1);
                    733:                }
                    734:                ls->ls_scope = lu->scope;
                    735:        }
                    736:
                    737:        /* URL-decode optional attributes and the search filter */
                    738:        if (lu->attributes[0] != NULL) {
                    739:                for (i = 0; i < MAXATTR && lu->attributes[i] != NULL; i++)
                    740:                        if (url_decode(lu->attributes[i]) == NULL)
                    741:                                return (-1);
                    742:                ls->ls_attr = lu->attributes;
                    743:        }
                    744:        if (lu->filter != NULL) {
                    745:                if (url_decode(lu->filter) == NULL)
                    746:                        return (-1);
                    747:                ls->ls_filter = lu->filter;
                    748:        }
                    749:
                    750:        return (0);
                    751: }
                    752:
                    753: /* From usr.sbin/httpd/httpd.c */
                    754: const char *
                    755: url_decode(char *url)
                    756: {
                    757:        char            *p, *q;
                    758:        char             hex[3];
                    759:        unsigned long    x;
                    760:
                    761:        hex[2] = '\0';
                    762:        p = q = url;
                    763:
                    764:        while (*p != '\0') {
                    765:                switch (*p) {
                    766:                case '%':
                    767:                        /* Encoding character is followed by two hex chars */
                    768:                        if (!(isxdigit((unsigned char)p[1]) &&
                    769:                            isxdigit((unsigned char)p[2])))
                    770:                                return (NULL);
                    771:
                    772:                        hex[0] = p[1];
                    773:                        hex[1] = p[2];
                    774:
                    775:                        /*
                    776:                         * We don't have to validate "hex" because it is
                    777:                         * guaranteed to include two hex chars followed by nul.
                    778:                         */
                    779:                        x = strtoul(hex, NULL, 16);
                    780:                        *q = (char)x;
                    781:                        p += 2;
                    782:                        break;
                    783:                default:
                    784:                        *q = *p;
                    785:                        break;
                    786:                }
                    787:                p++;
                    788:                q++;
                    789:        }
                    790:        *q = '\0';
                    791:
                    792:        return (url);
                    793: }