Index: head/usr.sbin/ypldap/aldap.c =================================================================== --- head/usr.sbin/ypldap/aldap.c (revision 300638) +++ head/usr.sbin/ypldap/aldap.c (revision 300639) @@ -1,1261 +1,1268 @@ /* $FreeBSD$ */ /* $Id: aldap.c,v 1.32 2016/04/27 10:53:27 schwarze Exp $ */ /* $OpenBSD: aldap.c,v 1.32 2016/04/27 10:53:27 schwarze Exp $ */ /* * Copyright (c) 2008 Alexander Schrijver * Copyright (c) 2006, 2007 Marc Balmer * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include "aldap.h" #if 0 #define DEBUG #endif #define VERSION 3 static struct ber_element *ldap_parse_search_filter(struct ber_element *, char *); static struct ber_element *ldap_do_parse_search_filter( struct ber_element *, char **); char **aldap_get_stringset(struct ber_element *); char *utoa(char *); static int isu8cont(unsigned char); char *parseval(char *, size_t); int aldap_create_page_control(struct ber_element *, int, struct aldap_page_control *); #ifdef DEBUG void ldap_debug_elements(struct ber_element *); #endif #ifdef DEBUG #define DPRINTF(x...) printf(x) #define LDAP_DEBUG(x, y) do { fprintf(stderr, "*** " x "\n"); ldap_debug_elements(y); } while (0) #else #define DPRINTF(x...) do { } while (0) #define LDAP_DEBUG(x, y) do { } while (0) #endif int aldap_close(struct aldap *al) { if (close(al->ber.fd) == -1) return (-1); ber_free(&al->ber); free(al); return (0); } struct aldap * aldap_init(int fd) { struct aldap *a; if ((a = calloc(1, sizeof(*a))) == NULL) return NULL; a->ber.fd = fd; return a; } int aldap_bind(struct aldap *ldap, char *binddn, char *bindcred) { struct ber_element *root = NULL, *elm; int error; if (binddn == NULL) binddn = ""; if (bindcred == NULL) bindcred = ""; if ((root = ber_add_sequence(NULL)) == NULL) goto fail; elm = ber_printf_elements(root, "d{tdsst", ++ldap->msgid, BER_CLASS_APP, (unsigned long)LDAP_REQ_BIND, VERSION, binddn, bindcred, BER_CLASS_CONTEXT, (unsigned long)LDAP_AUTH_SIMPLE); if (elm == NULL) goto fail; LDAP_DEBUG("aldap_bind", root); error = ber_write_elements(&ldap->ber, root); ber_free_elements(root); root = NULL; if (error == -1) goto fail; return (ldap->msgid); fail: if (root != NULL) ber_free_elements(root); ldap->err = ALDAP_ERR_OPERATION_FAILED; return (-1); } int aldap_unbind(struct aldap *ldap) { struct ber_element *root = NULL, *elm; int error; if ((root = ber_add_sequence(NULL)) == NULL) goto fail; elm = ber_printf_elements(root, "d{t", ++ldap->msgid, BER_CLASS_APP, LDAP_REQ_UNBIND_30); if (elm == NULL) goto fail; LDAP_DEBUG("aldap_unbind", root); error = ber_write_elements(&ldap->ber, root); ber_free_elements(root); root = NULL; if (error == -1) goto fail; return (ldap->msgid); fail: if (root != NULL) ber_free_elements(root); ldap->err = ALDAP_ERR_OPERATION_FAILED; return (-1); } int aldap_search(struct aldap *ldap, char *basedn, enum scope scope, char *filter, char **attrs, int typesonly, int sizelimit, int timelimit, struct aldap_page_control *page) { struct ber_element *root = NULL, *ber, *c; int i, error; if ((root = ber_add_sequence(NULL)) == NULL) goto fail; ber = ber_printf_elements(root, "d{t", ++ldap->msgid, BER_CLASS_APP, (unsigned long) LDAP_REQ_SEARCH); if (ber == NULL) { ldap->err = ALDAP_ERR_OPERATION_FAILED; goto fail; } c = ber; ber = ber_printf_elements(ber, "sEEddb", basedn, (long long)scope, (long long)LDAP_DEREF_NEVER, sizelimit, timelimit, typesonly); if (ber == NULL) { ldap->err = ALDAP_ERR_OPERATION_FAILED; goto fail; } if ((ber = ldap_parse_search_filter(ber, filter)) == NULL) { ldap->err = ALDAP_ERR_PARSER_ERROR; goto fail; } if ((ber = ber_add_sequence(ber)) == NULL) goto fail; if (attrs != NULL) for (i = 0; attrs[i] != NULL; i++) { if ((ber = ber_add_string(ber, attrs[i])) == NULL) goto fail; } aldap_create_page_control(c, 100, page); LDAP_DEBUG("aldap_search", root); error = ber_write_elements(&ldap->ber, root); ber_free_elements(root); root = NULL; if (error == -1) { ldap->err = ALDAP_ERR_OPERATION_FAILED; goto fail; } return (ldap->msgid); fail: if (root != NULL) ber_free_elements(root); return (-1); } int aldap_create_page_control(struct ber_element *elm, int size, struct aldap_page_control *page) { int len; struct ber c; struct ber_element *ber = NULL; c.br_wbuf = NULL; c.fd = -1; ber = ber_add_sequence(NULL); if (page == NULL) { if (ber_printf_elements(ber, "ds", 50, "") == NULL) goto fail; } else { if (ber_printf_elements(ber, "dx", 50, page->cookie, page->cookie_len) == NULL) goto fail; } if ((len = ber_write_elements(&c, ber)) < 1) goto fail; if (ber_printf_elements(elm, "{t{sx", 2, 0, LDAP_PAGED_OID, c.br_wbuf, (size_t)len) == NULL) goto fail; ber_free_elements(ber); ber_free(&c); return len; fail: if (ber != NULL) ber_free_elements(ber); ber_free(&c); return (-1); } struct aldap_message * aldap_parse(struct aldap *ldap) { int class; unsigned long type; long long msgid = 0; struct aldap_message *m; struct ber_element *a = NULL, *ep; if ((m = calloc(1, sizeof(struct aldap_message))) == NULL) return NULL; if ((m->msg = ber_read_elements(&ldap->ber, NULL)) == NULL) goto parsefail; LDAP_DEBUG("message", m->msg); if (ber_scanf_elements(m->msg, "{ite", &msgid, &class, &type, &a) != 0) goto parsefail; m->msgid = msgid; m->message_type = type; m->protocol_op = a; switch (m->message_type) { case LDAP_RES_BIND: case LDAP_RES_MODIFY: case LDAP_RES_ADD: case LDAP_RES_DELETE: case LDAP_RES_MODRDN: case LDAP_RES_COMPARE: case LDAP_RES_SEARCH_RESULT: if (ber_scanf_elements(m->protocol_op, "{EeSeSe", &m->body.res.rescode, &m->dn, &m->body.res.diagmsg, &a) != 0) goto parsefail; if (m->body.res.rescode == LDAP_REFERRAL) if (ber_scanf_elements(a, "{e", &m->references) != 0) goto parsefail; if (m->msg->be_sub) { for (ep = m->msg->be_sub; ep != NULL; ep = ep->be_next) { ber_scanf_elements(ep, "t", &class, &type); if (class == 2 && type == 0) m->page = aldap_parse_page_control(ep->be_sub->be_sub, ep->be_sub->be_sub->be_len); } } else m->page = NULL; break; case LDAP_RES_SEARCH_ENTRY: if (ber_scanf_elements(m->protocol_op, "{eS{e", &m->dn, &m->body.search.attrs) != 0) goto parsefail; break; case LDAP_RES_SEARCH_REFERENCE: if (ber_scanf_elements(m->protocol_op, "{e", &m->references) != 0) goto parsefail; break; } return m; parsefail: ldap->err = ALDAP_ERR_PARSER_ERROR; aldap_freemsg(m); return NULL; } struct aldap_page_control * aldap_parse_page_control(struct ber_element *control, size_t len) { char *oid, *s; char *encoded; struct ber b; struct ber_element *elm; struct aldap_page_control *page; b.br_wbuf = NULL; b.fd = -1; ber_scanf_elements(control, "ss", &oid, &encoded); ber_set_readbuf(&b, encoded, control->be_next->be_len); elm = ber_read_elements(&b, NULL); if ((page = malloc(sizeof(struct aldap_page_control))) == NULL) { if (elm != NULL) ber_free_elements(elm); ber_free(&b); return NULL; } ber_scanf_elements(elm->be_sub, "is", &page->size, &s); page->cookie_len = elm->be_sub->be_next->be_len; if ((page->cookie = malloc(page->cookie_len)) == NULL) { if (elm != NULL) ber_free_elements(elm); ber_free(&b); free(page); return NULL; } memcpy(page->cookie, s, page->cookie_len); ber_free_elements(elm); ber_free(&b); return page; } void aldap_freepage(struct aldap_page_control *page) { free(page->cookie); free(page); } void aldap_freemsg(struct aldap_message *msg) { if (msg->msg) ber_free_elements(msg->msg); free(msg); } int aldap_get_resultcode(struct aldap_message *msg) { return msg->body.res.rescode; } char * aldap_get_dn(struct aldap_message *msg) { char *dn; if (msg->dn == NULL) return NULL; if (ber_get_string(msg->dn, &dn) == -1) return NULL; return utoa(dn); } char ** aldap_get_references(struct aldap_message *msg) { if (msg->references == NULL) return NULL; return aldap_get_stringset(msg->references); } void aldap_free_references(char **values) { int i; if (values == NULL) return; for (i = 0; values[i] != NULL; i++) free(values[i]); free(values); } char * aldap_get_diagmsg(struct aldap_message *msg) { char *s; if (msg->body.res.diagmsg == NULL) return NULL; if (ber_get_string(msg->body.res.diagmsg, &s) == -1) return NULL; return utoa(s); } int aldap_count_attrs(struct aldap_message *msg) { int i; struct ber_element *a; if (msg->body.search.attrs == NULL) return (-1); for (i = 0, a = msg->body.search.attrs; a != NULL && ber_get_eoc(a) != 0; i++, a = a->be_next) ; return i; } int aldap_first_attr(struct aldap_message *msg, char **outkey, char ***outvalues) { struct ber_element *b, *c; char *key; char **ret; if (msg->body.search.attrs == NULL) goto fail; if (ber_scanf_elements(msg->body.search.attrs, "{s(e)}e", &key, &b, &c) != 0) goto fail; msg->body.search.iter = msg->body.search.attrs->be_next; if ((ret = aldap_get_stringset(b)) == NULL) goto fail; (*outvalues) = ret; (*outkey) = utoa(key); return (1); fail: (*outkey) = NULL; (*outvalues) = NULL; return (-1); } int aldap_next_attr(struct aldap_message *msg, char **outkey, char ***outvalues) { struct ber_element *a, *b; char *key; char **ret; if (msg->body.search.iter == NULL) goto notfound; LDAP_DEBUG("attr", msg->body.search.iter); if (ber_get_eoc(msg->body.search.iter) == 0) goto notfound; if (ber_scanf_elements(msg->body.search.iter, "{s(e)}e", &key, &a, &b) != 0) goto fail; msg->body.search.iter = msg->body.search.iter->be_next; if ((ret = aldap_get_stringset(a)) == NULL) goto fail; (*outvalues) = ret; (*outkey) = utoa(key); return (1); fail: notfound: (*outkey) = NULL; (*outvalues) = NULL; return (-1); } int aldap_match_attr(struct aldap_message *msg, char *inkey, char ***outvalues) { struct ber_element *a, *b; char *descr = NULL; char **ret; if (msg->body.search.attrs == NULL) goto fail; LDAP_DEBUG("attr", msg->body.search.attrs); for (a = msg->body.search.attrs;;) { if (a == NULL) goto notfound; if (ber_get_eoc(a) == 0) goto notfound; if (ber_scanf_elements(a, "{s(e", &descr, &b) != 0) goto fail; if (strcasecmp(descr, inkey) == 0) goto attrfound; a = a->be_next; } attrfound: if ((ret = aldap_get_stringset(b)) == NULL) goto fail; (*outvalues) = ret; return (1); fail: notfound: (*outvalues) = NULL; return (-1); } int aldap_free_attr(char **values) { int i; if (values == NULL) return -1; for (i = 0; values[i] != NULL; i++) free(values[i]); free(values); return (1); } #if 0 void aldap_free_url(struct aldap_url *lu) { free(lu->buffer); free(lu->filter); } int aldap_parse_url(char *url, struct aldap_url *lu) { char *p, *forward, *forward2; const char *errstr = NULL; int i; if ((lu->buffer = p = strdup(url)) == NULL) return (-1); /* protocol */ if (strncasecmp(LDAP_URL, p, strlen(LDAP_URL)) != 0) goto fail; lu->protocol = LDAP; p += strlen(LDAP_URL); /* host and optional port */ if ((forward = strchr(p, '/')) != NULL) *forward = '\0'; /* find the optional port */ if ((forward2 = strchr(p, ':')) != NULL) { *forward2 = '\0'; /* if a port is given */ if (*(forward2+1) != '\0') { #define PORT_MAX UINT16_MAX lu->port = strtonum(++forward2, 0, PORT_MAX, &errstr); if (errstr) goto fail; } } /* fail if no host is given */ if (strlen(p) == 0) goto fail; lu->host = p; if (forward == NULL) goto done; /* p is assigned either a pointer to a character or to '\0' */ p = ++forward; if (strlen(p) == 0) goto done; /* dn */ if ((forward = strchr(p, '?')) != NULL) *forward = '\0'; lu->dn = p; if (forward == NULL) goto done; /* p is assigned either a pointer to a character or to '\0' */ p = ++forward; if (strlen(p) == 0) goto done; /* attributes */ if ((forward = strchr(p, '?')) != NULL) *forward = '\0'; for (i = 0; i < MAXATTR; i++) { if ((forward2 = strchr(p, ',')) == NULL) { if (strlen(p) == 0) break; lu->attributes[i] = p; break; } *forward2 = '\0'; lu->attributes[i] = p; p = ++forward2; } if (forward == NULL) goto done; /* p is assigned either a pointer to a character or to '\0' */ p = ++forward; if (strlen(p) == 0) goto done; /* scope */ if ((forward = strchr(p, '?')) != NULL) *forward = '\0'; if (strcmp(p, "base") == 0) lu->scope = LDAP_SCOPE_BASE; else if (strcmp(p, "one") == 0) lu->scope = LDAP_SCOPE_ONELEVEL; else if (strcmp(p, "sub") == 0) lu->scope = LDAP_SCOPE_SUBTREE; else goto fail; if (forward == NULL) goto done; p = ++forward; if (strlen(p) == 0) goto done; /* filter */ if (p) lu->filter = p; done: free(url); return (1); fail: free(lu->buffer); lu->buffer = NULL; return (-1); } int aldap_search_url(struct aldap *ldap, char *url, int typesonly, int sizelimit, int timelimit) { struct aldap_url *lu; if ((lu = calloc(1, sizeof(*lu))) == NULL) return (-1); if (aldap_parse_url(url, lu)) goto fail; if (aldap_search(ldap, lu->dn, lu->scope, lu->filter, lu->attributes, typesonly, sizelimit, timelimit) == -1) goto fail; aldap_free_url(lu); return (ldap->msgid); fail: aldap_free_url(lu); return (-1); } #endif /* 0 */ /* * internal functions */ char ** aldap_get_stringset(struct ber_element *elm) { struct ber_element *a; int i; char **ret; char *s; if (elm->be_type != BER_TYPE_OCTETSTRING) return NULL; for (a = elm, i = 1; i > 0 && a != NULL && a->be_type == BER_TYPE_OCTETSTRING; a = a->be_next, i++) ; if (i == 1) return NULL; if ((ret = calloc(i + 1, sizeof(char *))) == NULL) return NULL; for (a = elm, i = 0; a != NULL && a->be_type == BER_TYPE_OCTETSTRING; - a = a->be_next, i++) { + a = a->be_next) { ber_get_string(a, &s); ret[i] = utoa(s); + if (ret[i] != NULL) + i++; + } - ret[i + 1] = NULL; + if (i == 0) { + free(ret); + return NULL; + } + ret[i] = NULL; return ret; } /* * Base case for ldap_do_parse_search_filter * * returns: * struct ber_element *, ber_element tree * NULL, parse failed */ static struct ber_element * ldap_parse_search_filter(struct ber_element *ber, char *filter) { struct ber_element *elm; char *cp; cp = filter; if (cp == NULL || *cp == '\0') { errno = EINVAL; return (NULL); } if ((elm = ldap_do_parse_search_filter(ber, &cp)) == NULL) return (NULL); if (*cp != '\0') { ber_free_elements(elm); ber_link_elements(ber, NULL); errno = EINVAL; return (NULL); } return (elm); } /* * Translate RFC4515 search filter string into ber_element tree * * returns: * struct ber_element *, ber_element tree * NULL, parse failed * * notes: * when cp is passed to a recursive invocation, it is updated * to point one character beyond the filter that was passed * i.e., cp jumps to "(filter)" upon return * ^ * goto's used to discriminate error-handling based on error type * doesn't handle extended filters (yet) * */ static struct ber_element * ldap_do_parse_search_filter(struct ber_element *prev, char **cpp) { struct ber_element *elm, *root = NULL; char *attr_desc, *attr_val, *parsed_val, *cp; size_t len; unsigned long type; root = NULL; /* cpp should pass in pointer to opening parenthesis of "(filter)" */ cp = *cpp; if (*cp != '(') goto syntaxfail; switch (*++cp) { case '&': /* AND */ case '|': /* OR */ if (*cp == '&') type = LDAP_FILT_AND; else type = LDAP_FILT_OR; if ((elm = ber_add_set(prev)) == NULL) goto callfail; root = elm; ber_set_header(elm, BER_CLASS_CONTEXT, type); if (*++cp != '(') /* opening `(` of filter */ goto syntaxfail; while (*cp == '(') { if ((elm = ldap_do_parse_search_filter(elm, &cp)) == NULL) goto bad; } if (*cp != ')') /* trailing `)` of filter */ goto syntaxfail; break; case '!': /* NOT */ if ((root = ber_add_sequence(prev)) == NULL) goto callfail; ber_set_header(root, BER_CLASS_CONTEXT, LDAP_FILT_NOT); cp++; /* now points to sub-filter */ if ((elm = ldap_do_parse_search_filter(root, &cp)) == NULL) goto bad; if (*cp != ')') /* trailing `)` of filter */ goto syntaxfail; break; default: /* SIMPLE || PRESENCE */ attr_desc = cp; len = strcspn(cp, "()<>~="); cp += len; switch (*cp) { case '~': type = LDAP_FILT_APPR; cp++; break; case '<': type = LDAP_FILT_LE; cp++; break; case '>': type = LDAP_FILT_GE; cp++; break; case '=': type = LDAP_FILT_EQ; /* assume EQ until disproven */ break; case '(': case ')': default: goto syntaxfail; } attr_val = ++cp; /* presence filter */ if (strncmp(attr_val, "*)", 2) == 0) { cp++; /* point to trailing `)` */ if ((root = ber_add_nstring(prev, attr_desc, len)) == NULL) goto bad; ber_set_header(root, BER_CLASS_CONTEXT, LDAP_FILT_PRES); break; } if ((root = ber_add_sequence(prev)) == NULL) goto callfail; ber_set_header(root, BER_CLASS_CONTEXT, type); if ((elm = ber_add_nstring(root, attr_desc, len)) == NULL) goto callfail; len = strcspn(attr_val, "*)"); if (len == 0 && *cp != '*') goto syntaxfail; cp += len; if (*cp == '\0') goto syntaxfail; if (*cp == '*') { /* substring filter */ int initial; cp = attr_val; ber_set_header(root, BER_CLASS_CONTEXT, LDAP_FILT_SUBS); if ((elm = ber_add_sequence(elm)) == NULL) goto callfail; for (initial = 1;; cp++, initial = 0) { attr_val = cp; len = strcspn(attr_val, "*)"); if (len == 0) { if (*cp == ')') break; else continue; } cp += len; if (*cp == '\0') goto syntaxfail; if (initial) type = LDAP_FILT_SUBS_INIT; else if (*cp == ')') type = LDAP_FILT_SUBS_FIN; else type = LDAP_FILT_SUBS_ANY; if ((parsed_val = parseval(attr_val, len)) == NULL) goto callfail; elm = ber_add_nstring(elm, parsed_val, strlen(parsed_val)); free(parsed_val); if (elm == NULL) goto callfail; ber_set_header(elm, BER_CLASS_CONTEXT, type); if (type == LDAP_FILT_SUBS_FIN) break; } break; } if ((parsed_val = parseval(attr_val, len)) == NULL) goto callfail; elm = ber_add_nstring(elm, parsed_val, strlen(parsed_val)); free(parsed_val); if (elm == NULL) goto callfail; break; } cp++; /* now points one char beyond the trailing `)` */ *cpp = cp; return (root); syntaxfail: /* XXX -- error reporting */ callfail: bad: if (root != NULL) ber_free_elements(root); ber_link_elements(prev, NULL); return (NULL); } #ifdef DEBUG /* * Display a list of ber elements. * */ void ldap_debug_elements(struct ber_element *root) { static int indent = 0; long long v; int d; char *buf; size_t len; u_int i; int constructed; struct ber_oid o; /* calculate lengths */ ber_calc_len(root); switch (root->be_encoding) { case BER_TYPE_SEQUENCE: case BER_TYPE_SET: constructed = root->be_encoding; break; default: constructed = 0; break; } fprintf(stderr, "%*slen %lu ", indent, "", root->be_len); switch (root->be_class) { case BER_CLASS_UNIVERSAL: fprintf(stderr, "class: universal(%u) type: ", root->be_class); switch (root->be_type) { case BER_TYPE_EOC: fprintf(stderr, "end-of-content"); break; case BER_TYPE_BOOLEAN: fprintf(stderr, "boolean"); break; case BER_TYPE_INTEGER: fprintf(stderr, "integer"); break; case BER_TYPE_BITSTRING: fprintf(stderr, "bit-string"); break; case BER_TYPE_OCTETSTRING: fprintf(stderr, "octet-string"); break; case BER_TYPE_NULL: fprintf(stderr, "null"); break; case BER_TYPE_OBJECT: fprintf(stderr, "object"); break; case BER_TYPE_ENUMERATED: fprintf(stderr, "enumerated"); break; case BER_TYPE_SEQUENCE: fprintf(stderr, "sequence"); break; case BER_TYPE_SET: fprintf(stderr, "set"); break; } break; case BER_CLASS_APPLICATION: fprintf(stderr, "class: application(%u) type: ", root->be_class); switch (root->be_type) { case LDAP_REQ_BIND: fprintf(stderr, "bind"); break; case LDAP_RES_BIND: fprintf(stderr, "bind"); break; case LDAP_REQ_UNBIND_30: break; case LDAP_REQ_SEARCH: fprintf(stderr, "search"); break; case LDAP_RES_SEARCH_ENTRY: fprintf(stderr, "search_entry"); break; case LDAP_RES_SEARCH_RESULT: fprintf(stderr, "search_result"); break; case LDAP_REQ_MODIFY: fprintf(stderr, "modify"); break; case LDAP_RES_MODIFY: fprintf(stderr, "modify"); break; case LDAP_REQ_ADD: fprintf(stderr, "add"); break; case LDAP_RES_ADD: fprintf(stderr, "add"); break; case LDAP_REQ_DELETE_30: fprintf(stderr, "delete"); break; case LDAP_RES_DELETE: fprintf(stderr, "delete"); break; case LDAP_REQ_MODRDN: fprintf(stderr, "modrdn"); break; case LDAP_RES_MODRDN: fprintf(stderr, "modrdn"); break; case LDAP_REQ_COMPARE: fprintf(stderr, "compare"); break; case LDAP_RES_COMPARE: fprintf(stderr, "compare"); break; case LDAP_REQ_ABANDON_30: fprintf(stderr, "abandon"); break; } break; case BER_CLASS_PRIVATE: fprintf(stderr, "class: private(%u) type: ", root->be_class); fprintf(stderr, "encoding (%lu) type: ", root->be_encoding); break; case BER_CLASS_CONTEXT: /* XXX: this is not correct */ fprintf(stderr, "class: context(%u) type: ", root->be_class); switch(root->be_type) { case LDAP_AUTH_SIMPLE: fprintf(stderr, "auth simple"); break; } break; default: fprintf(stderr, "class: (%u) type: ", root->be_class); break; } fprintf(stderr, "(%lu) encoding %lu ", root->be_type, root->be_encoding); if (constructed) root->be_encoding = constructed; switch (root->be_encoding) { case BER_TYPE_BOOLEAN: if (ber_get_boolean(root, &d) == -1) { fprintf(stderr, "\n"); break; } fprintf(stderr, "%s(%d)\n", d ? "true" : "false", d); break; case BER_TYPE_INTEGER: if (ber_get_integer(root, &v) == -1) { fprintf(stderr, "\n"); break; } fprintf(stderr, "value %lld\n", v); break; case BER_TYPE_ENUMERATED: if (ber_get_enumerated(root, &v) == -1) { fprintf(stderr, "\n"); break; } fprintf(stderr, "value %lld\n", v); break; case BER_TYPE_BITSTRING: if (ber_get_bitstring(root, (void *)&buf, &len) == -1) { fprintf(stderr, "\n"); break; } fprintf(stderr, "hexdump "); for (i = 0; i < len; i++) fprintf(stderr, "%02x", buf[i]); fprintf(stderr, "\n"); break; case BER_TYPE_OBJECT: if (ber_get_oid(root, &o) == -1) { fprintf(stderr, "\n"); break; } fprintf(stderr, "\n"); break; case BER_TYPE_OCTETSTRING: if (ber_get_nstring(root, (void *)&buf, &len) == -1) { fprintf(stderr, "\n"); break; } fprintf(stderr, "string \"%.*s\"\n", len, buf); break; case BER_TYPE_NULL: /* no payload */ case BER_TYPE_EOC: case BER_TYPE_SEQUENCE: case BER_TYPE_SET: default: fprintf(stderr, "\n"); break; } if (constructed && root->be_sub) { indent += 2; ldap_debug_elements(root->be_sub); indent -= 2; } if (root->be_next) ldap_debug_elements(root->be_next); } #endif /* * Strip UTF-8 down to ASCII without validation. * notes: * non-ASCII characters are displayed as '?' * the argument u should be a NULL terminated sequence of UTF-8 bytes. */ char * utoa(char *u) { int len, i, j; char *str; /* calculate the length to allocate */ for (len = 0, i = 0; u[i] != '\0'; i++) if (!isu8cont(u[i])) len++; if ((str = calloc(len + 1, sizeof(char))) == NULL) return NULL; /* copy the ASCII characters to the newly allocated string */ for (i = 0, j = 0; u[i] != '\0'; i++) if (!isu8cont(u[i])) str[j++] = isascii((unsigned char)u[i]) ? u[i] : '?'; return str; } static int isu8cont(unsigned char c) { return (c & (0x80 | 0x40)) == 0x80; } /* * Parse a LDAP value * notes: * the argument u should be a NULL terminated sequence of ASCII bytes. */ char * parseval(char *p, size_t len) { char hex[3]; char *cp = p, *buffer, *newbuffer; size_t size, newsize, i, j; size = 50; if ((buffer = calloc(1, size)) == NULL) return NULL; for (i = j = 0; j < len; i++) { if (i >= size) { newsize = size + 1024; if ((newbuffer = realloc(buffer, newsize)) == NULL) { free(buffer); return (NULL); } buffer = newbuffer; size = newsize; } if (cp[j] == '\\') { strlcpy(hex, cp + j + 1, sizeof(hex)); buffer[i] = (char)strtoumax(hex, NULL, 16); j += 3; } else { buffer[i] = cp[j]; j++; } } return buffer; } int aldap_get_errno(struct aldap *a, const char **estr) { switch (a->err) { case ALDAP_ERR_SUCCESS: *estr = "success"; break; case ALDAP_ERR_PARSER_ERROR: *estr = "parser failed"; break; case ALDAP_ERR_INVALID_FILTER: *estr = "invalid filter"; break; case ALDAP_ERR_OPERATION_FAILED: *estr = "operation failed"; break; default: *estr = "unknown"; break; } return (a->err); } Index: head/usr.sbin/ypldap/ber.c =================================================================== --- head/usr.sbin/ypldap/ber.c (revision 300638) +++ head/usr.sbin/ypldap/ber.c (revision 300639) @@ -1,1277 +1,1276 @@ /* $OpenBSD: ber.c,v 1.9 2015/02/12 00:30:38 pelikan Exp $ */ /* $FreeBSD$ */ /* * Copyright (c) 2007 Reyk Floeter * Copyright (c) 2006, 2007 Claudio Jeker * Copyright (c) 2006, 2007 Marc Balmer * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include /* XXX for debug output */ #include /* XXX for debug output */ #include #include #include #include "ber.h" #define MINIMUM(a, b) (((a) < (b)) ? (a) : (b)) #define BER_TYPE_CONSTRUCTED 0x20 /* otherwise primitive */ #define BER_TYPE_SINGLE_MAX 30 #define BER_TAG_MASK 0x1f #define BER_TAG_MORE 0x80 /* more subsequent octets */ #define BER_TAG_TYPE_MASK 0x7f #define BER_CLASS_SHIFT 6 static int ber_dump_element(struct ber *ber, struct ber_element *root); static void ber_dump_header(struct ber *ber, struct ber_element *root); static void ber_putc(struct ber *ber, u_char c); static void ber_write(struct ber *ber, void *buf, size_t len); static ssize_t get_id(struct ber *b, unsigned long *tag, int *class, int *cstruct); static ssize_t get_len(struct ber *b, ssize_t *len); static ssize_t ber_read_element(struct ber *ber, struct ber_element *elm); static ssize_t ber_readbuf(struct ber *b, void *buf, size_t nbytes); static ssize_t ber_getc(struct ber *b, u_char *c); static ssize_t ber_read(struct ber *ber, void *buf, size_t len); #ifdef DEBUG #define DPRINTF(...) printf(__VA_ARGS__) #else #define DPRINTF(...) do { } while (0) #endif struct ber_element * ber_get_element(unsigned long encoding) { struct ber_element *elm; if ((elm = calloc(1, sizeof(*elm))) == NULL) return NULL; elm->be_encoding = encoding; ber_set_header(elm, BER_CLASS_UNIVERSAL, BER_TYPE_DEFAULT); return elm; } void ber_set_header(struct ber_element *elm, int class, unsigned long type) { elm->be_class = class & BER_CLASS_MASK; if (type == BER_TYPE_DEFAULT) type = elm->be_encoding; elm->be_type = type; } void ber_link_elements(struct ber_element *prev, struct ber_element *elm) { if (prev != NULL) { if ((prev->be_encoding == BER_TYPE_SEQUENCE || prev->be_encoding == BER_TYPE_SET) && prev->be_sub == NULL) prev->be_sub = elm; else prev->be_next = elm; } } struct ber_element * ber_unlink_elements(struct ber_element *prev) { struct ber_element *elm; if ((prev->be_encoding == BER_TYPE_SEQUENCE || prev->be_encoding == BER_TYPE_SET) && prev->be_sub != NULL) { elm = prev->be_sub; prev->be_sub = NULL; } else { elm = prev->be_next; prev->be_next = NULL; } return (elm); } void ber_replace_elements(struct ber_element *prev, struct ber_element *new) { struct ber_element *ber, *next; ber = ber_unlink_elements(prev); next = ber_unlink_elements(ber); ber_link_elements(new, next); ber_link_elements(prev, new); /* cleanup old element */ ber_free_elements(ber); } struct ber_element * ber_add_sequence(struct ber_element *prev) { struct ber_element *elm; if ((elm = ber_get_element(BER_TYPE_SEQUENCE)) == NULL) return NULL; ber_link_elements(prev, elm); return elm; } struct ber_element * ber_add_set(struct ber_element *prev) { struct ber_element *elm; if ((elm = ber_get_element(BER_TYPE_SET)) == NULL) return NULL; ber_link_elements(prev, elm); return elm; } struct ber_element * ber_add_enumerated(struct ber_element *prev, long long val) { struct ber_element *elm; u_int i, len = 0; u_char cur, last = 0; if ((elm = ber_get_element(BER_TYPE_ENUMERATED)) == NULL) return NULL; elm->be_numeric = val; for (i = 0; i < sizeof(long long); i++) { cur = val & 0xff; if (cur != 0 && cur != 0xff) len = i; if ((cur == 0 && last & 0x80) || (cur == 0xff && (last & 0x80) == 0)) len = i; val >>= 8; last = cur; } elm->be_len = len + 1; ber_link_elements(prev, elm); return elm; } struct ber_element * ber_add_integer(struct ber_element *prev, long long val) { struct ber_element *elm; u_int i, len = 0; u_char cur, last = 0; if ((elm = ber_get_element(BER_TYPE_INTEGER)) == NULL) return NULL; elm->be_numeric = val; for (i = 0; i < sizeof(long long); i++) { cur = val & 0xff; if (cur != 0 && cur != 0xff) len = i; if ((cur == 0 && last & 0x80) || (cur == 0xff && (last & 0x80) == 0)) len = i; val >>= 8; last = cur; } elm->be_len = len + 1; ber_link_elements(prev, elm); return elm; } int ber_get_integer(struct ber_element *elm, long long *n) { if (elm->be_encoding != BER_TYPE_INTEGER) return -1; *n = elm->be_numeric; return 0; } int ber_get_enumerated(struct ber_element *elm, long long *n) { if (elm->be_encoding != BER_TYPE_ENUMERATED) return -1; *n = elm->be_numeric; return 0; } struct ber_element * ber_add_boolean(struct ber_element *prev, int bool) { struct ber_element *elm; if ((elm = ber_get_element(BER_TYPE_BOOLEAN)) == NULL) return NULL; elm->be_numeric = bool ? 0xff : 0; elm->be_len = 1; ber_link_elements(prev, elm); return elm; } int ber_get_boolean(struct ber_element *elm, int *b) { if (elm->be_encoding != BER_TYPE_BOOLEAN) return -1; *b = !(elm->be_numeric == 0); return 0; } struct ber_element * ber_add_string(struct ber_element *prev, const char *string) { return ber_add_nstring(prev, string, strlen(string)); } struct ber_element * ber_add_nstring(struct ber_element *prev, const char *string0, size_t len) { struct ber_element *elm; char *string; if ((string = calloc(1, len)) == NULL) return NULL; if ((elm = ber_get_element(BER_TYPE_OCTETSTRING)) == NULL) { free(string); return NULL; } bcopy(string0, string, len); elm->be_val = string; elm->be_len = len; elm->be_free = 1; /* free string on cleanup */ ber_link_elements(prev, elm); return elm; } int ber_get_string(struct ber_element *elm, char **s) { if (elm->be_encoding != BER_TYPE_OCTETSTRING) return -1; *s = elm->be_val; return 0; } int ber_get_nstring(struct ber_element *elm, void **p, size_t *len) { if (elm->be_encoding != BER_TYPE_OCTETSTRING) return -1; *p = elm->be_val; *len = elm->be_len; return 0; } struct ber_element * ber_add_bitstring(struct ber_element *prev, const void *v0, size_t len) { struct ber_element *elm; void *v; if ((v = calloc(1, len)) == NULL) return NULL; if ((elm = ber_get_element(BER_TYPE_BITSTRING)) == NULL) { free(v); return NULL; } bcopy(v0, v, len); elm->be_val = v; elm->be_len = len; elm->be_free = 1; /* free string on cleanup */ ber_link_elements(prev, elm); return elm; } int ber_get_bitstring(struct ber_element *elm, void **v, size_t *len) { if (elm->be_encoding != BER_TYPE_BITSTRING) return -1; *v = elm->be_val; *len = elm->be_len; return 0; } struct ber_element * ber_add_null(struct ber_element *prev) { struct ber_element *elm; if ((elm = ber_get_element(BER_TYPE_NULL)) == NULL) return NULL; ber_link_elements(prev, elm); return elm; } int ber_get_null(struct ber_element *elm) { if (elm->be_encoding != BER_TYPE_NULL) return -1; return 0; } struct ber_element * ber_add_eoc(struct ber_element *prev) { struct ber_element *elm; if ((elm = ber_get_element(BER_TYPE_EOC)) == NULL) return NULL; ber_link_elements(prev, elm); return elm; } int ber_get_eoc(struct ber_element *elm) { if (elm->be_encoding != BER_TYPE_EOC) return -1; return 0; } size_t ber_oid2ber(struct ber_oid *o, u_int8_t *buf, size_t len) { u_int32_t v; u_int i, j = 0, k; if (o->bo_n < BER_MIN_OID_LEN || o->bo_n > BER_MAX_OID_LEN || o->bo_id[0] > 2 || o->bo_id[1] > 40) return (0); v = (o->bo_id[0] * 40) + o->bo_id[1]; for (i = 2, j = 0; i <= o->bo_n; v = o->bo_id[i], i++) { for (k = 28; k >= 7; k -= 7) { if (v >= (u_int)(1 << k)) { if (len) buf[j] = v >> k | BER_TAG_MORE; j++; } } if (len) buf[j] = v & BER_TAG_TYPE_MASK; j++; } return (j); } int ber_string2oid(const char *oidstr, struct ber_oid *o) { char *sp, *p, str[BUFSIZ]; const char *errstr; if (strlcpy(str, oidstr, sizeof(str)) >= sizeof(str)) return (-1); bzero(o, sizeof(*o)); /* Parse OID strings in the common forms n.n.n, n_n_n_n, or n-n-n */ for (p = sp = str; p != NULL; sp = p) { if ((p = strpbrk(p, "._-")) != NULL) *p++ = '\0'; o->bo_id[o->bo_n++] = strtonum(sp, 0, UINT_MAX, &errstr); if (errstr || o->bo_n > BER_MAX_OID_LEN) return (-1); } return (0); } struct ber_element * ber_add_oid(struct ber_element *prev, struct ber_oid *o) { struct ber_element *elm; u_int8_t *buf; size_t len; if ((elm = ber_get_element(BER_TYPE_OBJECT)) == NULL) return (NULL); if ((len = ber_oid2ber(o, NULL, 0)) == 0) goto fail; if ((buf = calloc(1, len)) == NULL) goto fail; elm->be_val = buf; elm->be_len = len; elm->be_free = 1; if (ber_oid2ber(o, buf, len) != len) goto fail; ber_link_elements(prev, elm); return (elm); fail: ber_free_elements(elm); return (NULL); } struct ber_element * ber_add_noid(struct ber_element *prev, struct ber_oid *o, int n) { struct ber_oid no; if (n > BER_MAX_OID_LEN) return (NULL); no.bo_n = n; bcopy(&o->bo_id, &no.bo_id, sizeof(no.bo_id)); return (ber_add_oid(prev, &no)); } struct ber_element * ber_add_oidstring(struct ber_element *prev, const char *oidstr) { struct ber_oid o; if (ber_string2oid(oidstr, &o) == -1) return (NULL); return (ber_add_oid(prev, &o)); } int ber_get_oid(struct ber_element *elm, struct ber_oid *o) { u_int8_t *buf; size_t len, i = 0, j = 0; if (elm->be_encoding != BER_TYPE_OBJECT) return (-1); buf = elm->be_val; len = elm->be_len; if (!buf[i]) return (-1); bzero(o, sizeof(*o)); o->bo_id[j++] = buf[i] / 40; o->bo_id[j++] = buf[i++] % 40; for (; i < len && j < BER_MAX_OID_LEN; i++) { o->bo_id[j] = (o->bo_id[j] << 7) + (buf[i] & ~0x80); if (buf[i] & 0x80) continue; j++; } o->bo_n = j; return (0); } struct ber_element * ber_printf_elements(struct ber_element *ber, char *fmt, ...) { va_list ap; int d, class; size_t len; unsigned long type; long long i; char *s; void *p; struct ber_oid *o; struct ber_element *sub = ber, *e; va_start(ap, fmt); while (*fmt) { switch (*fmt++) { case 'B': p = va_arg(ap, void *); len = va_arg(ap, size_t); if ((ber = ber_add_bitstring(ber, p, len)) == NULL) goto fail; break; case 'b': d = va_arg(ap, int); if ((ber = ber_add_boolean(ber, d)) == NULL) goto fail; break; case 'd': d = va_arg(ap, int); if ((ber = ber_add_integer(ber, d)) == NULL) goto fail; break; case 'e': e = va_arg(ap, struct ber_element *); ber_link_elements(ber, e); break; case 'E': i = va_arg(ap, long long); if ((ber = ber_add_enumerated(ber, i)) == NULL) goto fail; break; case 'i': i = va_arg(ap, long long); if ((ber = ber_add_integer(ber, i)) == NULL) goto fail; break; case 'O': o = va_arg(ap, struct ber_oid *); if ((ber = ber_add_oid(ber, o)) == NULL) goto fail; break; case 'o': s = va_arg(ap, char *); if ((ber = ber_add_oidstring(ber, s)) == NULL) goto fail; break; case 's': s = va_arg(ap, char *); if ((ber = ber_add_string(ber, s)) == NULL) goto fail; break; case 't': class = va_arg(ap, int); type = va_arg(ap, unsigned long); ber_set_header(ber, class, type); break; case 'x': s = va_arg(ap, char *); len = va_arg(ap, size_t); if ((ber = ber_add_nstring(ber, s, len)) == NULL) goto fail; break; case '0': if ((ber = ber_add_null(ber)) == NULL) goto fail; break; case '{': if ((ber = sub = ber_add_sequence(ber)) == NULL) goto fail; break; case '(': if ((ber = sub = ber_add_set(ber)) == NULL) goto fail; break; case '}': case ')': ber = sub; break; case '.': if ((e = ber_add_eoc(ber)) == NULL) goto fail; ber = e; break; default: break; } } va_end(ap); return (ber); fail: - ber_free_elements(ber); return (NULL); } int ber_scanf_elements(struct ber_element *ber, char *fmt, ...) { #define _MAX_SEQ 128 va_list ap; int *d, level = -1; unsigned long *t; long long *i; void **ptr; size_t *len, ret = 0, n = strlen(fmt); char **s; struct ber_oid *o; struct ber_element *parent[_MAX_SEQ], **e; bzero(parent, sizeof(struct ber_element *) * _MAX_SEQ); va_start(ap, fmt); while (*fmt) { switch (*fmt++) { case 'B': ptr = va_arg(ap, void **); len = va_arg(ap, size_t *); if (ber_get_bitstring(ber, ptr, len) == -1) goto fail; ret++; break; case 'b': d = va_arg(ap, int *); if (ber_get_boolean(ber, d) == -1) goto fail; ret++; break; case 'e': e = va_arg(ap, struct ber_element **); *e = ber; ret++; continue; case 'E': i = va_arg(ap, long long *); if (ber_get_enumerated(ber, i) == -1) goto fail; ret++; break; case 'i': i = va_arg(ap, long long *); if (ber_get_integer(ber, i) == -1) goto fail; ret++; break; case 'o': o = va_arg(ap, struct ber_oid *); if (ber_get_oid(ber, o) == -1) goto fail; ret++; break; case 'S': ret++; break; case 's': s = va_arg(ap, char **); if (ber_get_string(ber, s) == -1) goto fail; ret++; break; case 't': d = va_arg(ap, int *); t = va_arg(ap, unsigned long *); *d = ber->be_class; *t = ber->be_type; ret++; continue; case 'x': ptr = va_arg(ap, void **); len = va_arg(ap, size_t *); if (ber_get_nstring(ber, ptr, len) == -1) goto fail; ret++; break; case '0': if (ber->be_encoding != BER_TYPE_NULL) goto fail; ret++; break; case '.': if (ber->be_encoding != BER_TYPE_EOC) goto fail; ret++; break; case '{': case '(': if (ber->be_encoding != BER_TYPE_SEQUENCE && ber->be_encoding != BER_TYPE_SET) goto fail; if (ber->be_sub == NULL || level >= _MAX_SEQ-1) goto fail; parent[++level] = ber; ber = ber->be_sub; ret++; continue; case '}': case ')': if (level < 0 || parent[level] == NULL) goto fail; ber = parent[level--]; ret++; continue; default: goto fail; } if (ber->be_next == NULL) continue; ber = ber->be_next; } va_end(ap); return (ret == n ? 0 : -1); fail: va_end(ap); return (-1); } /* * write ber elements to the socket * * params: * ber holds the socket * root fully populated element tree * * returns: * >=0 number of bytes written * -1 on failure and sets errno */ int ber_write_elements(struct ber *ber, struct ber_element *root) { size_t len; /* calculate length because only the definite form is required */ len = ber_calc_len(root); DPRINTF("write ber element of %zd bytes length\n", len); if (ber->br_wbuf != NULL && ber->br_wbuf + len > ber->br_wend) { free(ber->br_wbuf); ber->br_wbuf = NULL; } if (ber->br_wbuf == NULL) { if ((ber->br_wbuf = malloc(len)) == NULL) return -1; ber->br_wend = ber->br_wbuf + len; } /* reset write pointer */ ber->br_wptr = ber->br_wbuf; if (ber_dump_element(ber, root) == -1) return -1; /* XXX this should be moved to a different function */ if (ber->fd != -1) return write(ber->fd, ber->br_wbuf, len); return (len); } /* * read ber elements from the socket * * params: * ber holds the socket and lot more * root if NULL, build up an element tree from what we receive on * the wire. If not null, use the specified encoding for the * elements received. * * returns: * !=NULL, elements read and store in the ber_element tree * NULL, type mismatch or read error */ struct ber_element * ber_read_elements(struct ber *ber, struct ber_element *elm) { struct ber_element *root = elm; if (root == NULL) { if ((root = ber_get_element(0)) == NULL) return NULL; } DPRINTF("read ber elements, root %p\n", root); if (ber_read_element(ber, root) == -1) { /* Cleanup if root was allocated by us */ if (elm == NULL) ber_free_elements(root); return NULL; } return root; } void ber_free_elements(struct ber_element *root) { if (root->be_sub && (root->be_encoding == BER_TYPE_SEQUENCE || root->be_encoding == BER_TYPE_SET)) ber_free_elements(root->be_sub); if (root->be_next) ber_free_elements(root->be_next); if (root->be_free && (root->be_encoding == BER_TYPE_OCTETSTRING || root->be_encoding == BER_TYPE_BITSTRING || root->be_encoding == BER_TYPE_OBJECT)) free(root->be_val); free(root); } size_t ber_calc_len(struct ber_element *root) { unsigned long t; size_t s; size_t size = 2; /* minimum 1 byte head and 1 byte size */ /* calculate the real length of a sequence or set */ if (root->be_sub && (root->be_encoding == BER_TYPE_SEQUENCE || root->be_encoding == BER_TYPE_SET)) root->be_len = ber_calc_len(root->be_sub); /* fix header length for extended types */ if (root->be_type > BER_TYPE_SINGLE_MAX) for (t = root->be_type; t > 0; t >>= 7) size++; if (root->be_len >= BER_TAG_MORE) for (s = root->be_len; s > 0; s >>= 8) size++; /* calculate the length of the following elements */ if (root->be_next) size += ber_calc_len(root->be_next); /* This is an empty element, do not use a minimal size */ if (root->be_type == BER_TYPE_EOC && root->be_len == 0) return (0); return (root->be_len + size); } /* * internal functions */ static int ber_dump_element(struct ber *ber, struct ber_element *root) { unsigned long long l; int i; uint8_t u; ber_dump_header(ber, root); switch (root->be_encoding) { case BER_TYPE_BOOLEAN: case BER_TYPE_INTEGER: case BER_TYPE_ENUMERATED: l = (unsigned long long)root->be_numeric; for (i = root->be_len; i > 0; i--) { u = (l >> ((i - 1) * 8)) & 0xff; ber_putc(ber, u); } break; case BER_TYPE_BITSTRING: return -1; case BER_TYPE_OCTETSTRING: case BER_TYPE_OBJECT: ber_write(ber, root->be_val, root->be_len); break; case BER_TYPE_NULL: /* no payload */ case BER_TYPE_EOC: break; case BER_TYPE_SEQUENCE: case BER_TYPE_SET: if (root->be_sub && ber_dump_element(ber, root->be_sub) == -1) return -1; break; } if (root->be_next == NULL) return 0; return ber_dump_element(ber, root->be_next); } static void ber_dump_header(struct ber *ber, struct ber_element *root) { u_char id = 0, t, buf[8]; unsigned long type; size_t size; /* class universal, type encoding depending on type value */ /* length encoding */ if (root->be_type <= BER_TYPE_SINGLE_MAX) { id = root->be_type | (root->be_class << BER_CLASS_SHIFT); if (root->be_encoding == BER_TYPE_SEQUENCE || root->be_encoding == BER_TYPE_SET) id |= BER_TYPE_CONSTRUCTED; ber_putc(ber, id); } else { id = BER_TAG_MASK | (root->be_class << BER_CLASS_SHIFT); if (root->be_encoding == BER_TYPE_SEQUENCE || root->be_encoding == BER_TYPE_SET) id |= BER_TYPE_CONSTRUCTED; ber_putc(ber, id); for (t = 0, type = root->be_type; type > 0; type >>= 7) buf[t++] = type & ~BER_TAG_MORE; while (t-- > 0) { if (t > 0) buf[t] |= BER_TAG_MORE; ber_putc(ber, buf[t]); } } if (root->be_len < BER_TAG_MORE) { /* short form */ ber_putc(ber, root->be_len); } else { for (t = 0, size = root->be_len; size > 0; size >>= 8) buf[t++] = size & 0xff; ber_putc(ber, t | BER_TAG_MORE); while (t > 0) ber_putc(ber, buf[--t]); } } static void ber_putc(struct ber *ber, u_char c) { if (ber->br_wptr + 1 <= ber->br_wend) *ber->br_wptr = c; ber->br_wptr++; } static void ber_write(struct ber *ber, void *buf, size_t len) { if (ber->br_wptr + len <= ber->br_wend) bcopy(buf, ber->br_wptr, len); ber->br_wptr += len; } /* * extract a BER encoded tag. There are two types, a short and long form. */ static ssize_t get_id(struct ber *b, unsigned long *tag, int *class, int *cstruct) { u_char u; size_t i = 0; unsigned long t = 0; if (ber_getc(b, &u) == -1) return -1; *class = (u >> BER_CLASS_SHIFT) & BER_CLASS_MASK; *cstruct = (u & BER_TYPE_CONSTRUCTED) == BER_TYPE_CONSTRUCTED; if ((u & BER_TAG_MASK) != BER_TAG_MASK) { *tag = u & BER_TAG_MASK; return 1; } do { if (ber_getc(b, &u) == -1) return -1; t = (t << 7) | (u & ~BER_TAG_MORE); i++; } while (u & BER_TAG_MORE); if (i > sizeof(unsigned long)) { errno = ERANGE; return -1; } *tag = t; return i + 1; } /* * extract length of a ber object -- if length is unknown an error is returned. */ static ssize_t get_len(struct ber *b, ssize_t *len) { u_char u, n; ssize_t s, r; if (ber_getc(b, &u) == -1) return -1; if ((u & BER_TAG_MORE) == 0) { /* short form */ *len = u; return 1; } n = u & ~BER_TAG_MORE; if (sizeof(ssize_t) < n) { errno = ERANGE; return -1; } r = n + 1; for (s = 0; n > 0; n--) { if (ber_getc(b, &u) == -1) return -1; s = (s << 8) | u; } if (s < 0) { /* overflow */ errno = ERANGE; return -1; } if (s == 0) { /* invalid encoding */ errno = EINVAL; return -1; } *len = s; return r; } static ssize_t ber_read_element(struct ber *ber, struct ber_element *elm) { long long val = 0; struct ber_element *next; unsigned long type; int i, class, cstruct; ssize_t len, r, totlen = 0; u_char c; if ((r = get_id(ber, &type, &class, &cstruct)) == -1) return -1; DPRINTF("ber read got class %d type %lu, %s\n", class, type, cstruct ? "constructive" : "primitive"); totlen += r; if ((r = get_len(ber, &len)) == -1) return -1; DPRINTF("ber read element size %zd\n", len); totlen += r + len; /* * If using an external buffer and the total size of the element * is larger, then the external buffer don't bother to continue. */ if (ber->fd == -1 && len > ber->br_rend - ber->br_rptr) { errno = ECANCELED; return -1; } elm->be_type = type; elm->be_len = len; elm->be_class = class; if (elm->be_encoding == 0) { /* try to figure out the encoding via class, type and cstruct */ if (cstruct) elm->be_encoding = BER_TYPE_SEQUENCE; else if (class == BER_CLASS_UNIVERSAL) elm->be_encoding = type; else if (ber->br_application != NULL) { /* * Ask the application to map the encoding to a * universal type. For example, a SMI IpAddress * type is defined as 4 byte OCTET STRING. */ elm->be_encoding = (*ber->br_application)(elm); } else /* last resort option */ elm->be_encoding = BER_TYPE_NULL; } switch (elm->be_encoding) { case BER_TYPE_EOC: /* End-Of-Content */ break; case BER_TYPE_BOOLEAN: case BER_TYPE_INTEGER: case BER_TYPE_ENUMERATED: if (len > (ssize_t)sizeof(long long)) return -1; for (i = 0; i < len; i++) { if (ber_getc(ber, &c) != 1) return -1; val <<= 8; val |= c; } /* sign extend if MSB is set */ if (val >> ((i - 1) * 8) & 0x80) val |= ULLONG_MAX << (i * 8); elm->be_numeric = val; break; case BER_TYPE_BITSTRING: elm->be_val = malloc(len); if (elm->be_val == NULL) return -1; elm->be_free = 1; elm->be_len = len; ber_read(ber, elm->be_val, len); break; case BER_TYPE_OCTETSTRING: case BER_TYPE_OBJECT: elm->be_val = malloc(len + 1); if (elm->be_val == NULL) return -1; elm->be_free = 1; elm->be_len = len; ber_read(ber, elm->be_val, len); ((u_char *)elm->be_val)[len] = '\0'; break; case BER_TYPE_NULL: /* no payload */ if (len != 0) return -1; break; case BER_TYPE_SEQUENCE: case BER_TYPE_SET: if (elm->be_sub == NULL) { if ((elm->be_sub = ber_get_element(0)) == NULL) return -1; } next = elm->be_sub; while (len > 0) { r = ber_read_element(ber, next); if (r == -1) return -1; len -= r; if (len > 0 && next->be_next == NULL) { if ((next->be_next = ber_get_element(0)) == NULL) return -1; } next = next->be_next; } break; } return totlen; } static ssize_t ber_readbuf(struct ber *b, void *buf, size_t nbytes) { size_t sz; size_t len; if (b->br_rbuf == NULL) return -1; sz = b->br_rend - b->br_rptr; len = MINIMUM(nbytes, sz); if (len == 0) { errno = ECANCELED; return (-1); /* end of buffer and parser wants more data */ } bcopy(b->br_rptr, buf, len); b->br_rptr += len; return (len); } void ber_set_readbuf(struct ber *b, void *buf, size_t len) { b->br_rbuf = b->br_rptr = buf; b->br_rend = (u_int8_t *)buf + len; } ssize_t ber_get_writebuf(struct ber *b, void **buf) { if (b->br_wbuf == NULL) return -1; *buf = b->br_wbuf; return (b->br_wend - b->br_wbuf); } void ber_set_application(struct ber *b, unsigned long (*cb)(struct ber_element *)) { b->br_application = cb; } void ber_free(struct ber *b) { free(b->br_wbuf); } static ssize_t ber_getc(struct ber *b, u_char *c) { ssize_t r; /* * XXX calling read here is wrong in many ways. The most obvious one * being that we will block till data arrives. * But for now it is _good enough_ *gulp* */ if (b->fd == -1) r = ber_readbuf(b, c, 1); else r = read(b->fd, c, 1); return r; } static ssize_t ber_read(struct ber *ber, void *buf, size_t len) { u_char *b = buf; ssize_t r, remain = len; /* * XXX calling read here is wrong in many ways. The most obvious one * being that we will block till data arrives. * But for now it is _good enough_ *gulp* */ while (remain > 0) { if (ber->fd == -1) r = ber_readbuf(ber, b, remain); else r = read(ber->fd, b, remain); if (r == -1) { if (errno == EINTR || errno == EAGAIN) continue; return -1; } if (r == 0) return (b - (u_char *)buf); b += r; remain -= r; } return (b - (u_char *)buf); } Index: head/usr.sbin/ypldap/ldapclient.c =================================================================== --- head/usr.sbin/ypldap/ldapclient.c (revision 300638) +++ head/usr.sbin/ypldap/ldapclient.c (revision 300639) @@ -1,694 +1,692 @@ /* $OpenBSD: ldapclient.c,v 1.31 2014/11/16 23:24:44 tedu Exp $ */ /* $FreeBSD$ */ /* * Copyright (c) 2008 Alexander Schrijver * Copyright (c) 2008 Pierre-Yves Ritschard * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "aldap.h" #include "ypldap.h" void client_sig_handler(int, short, void *); void client_dispatch_dns(int, short, void *); void client_dispatch_parent(int, short, void *); void client_shutdown(void); void client_connect(int, short, void *); void client_configure(struct env *); void client_periodic_update(int, short, void *); int client_build_req(struct idm *, struct idm_req *, struct aldap_message *, int, int); int client_search_idm(struct env *, struct idm *, struct aldap *, char **, char *, int, int, enum imsg_type); int client_try_idm(struct env *, struct idm *); int client_addr_init(struct idm *); int client_addr_free(struct idm *); struct aldap *client_aldap_open(struct ypldap_addr_list *); /* * dummy wrapper to provide aldap_init with its fd's. */ struct aldap * client_aldap_open(struct ypldap_addr_list *addr) { int fd = -1; struct ypldap_addr *p; TAILQ_FOREACH(p, addr, next) { char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV]; struct sockaddr *sa = (struct sockaddr *)&p->ss; if (getnameinfo(sa, sa->sa_len, hbuf, sizeof(hbuf), sbuf, sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV)) errx(1, "could not get numeric hostname"); if ((fd = socket(sa->sa_family, SOCK_STREAM, 0)) < 0) return NULL; if (connect(fd, sa, sa->sa_len) == 0) break; warn("connect to %s port %s (%s) failed", hbuf, sbuf, "tcp"); close(fd); } if (fd == -1) return NULL; return aldap_init(fd); } int client_addr_init(struct idm *idm) { struct sockaddr_in *sa_in; struct sockaddr_in6 *sa_in6; struct ypldap_addr *h; TAILQ_FOREACH(h, &idm->idm_addr, next) { switch (h->ss.ss_family) { case AF_INET: sa_in = (struct sockaddr_in *)&h->ss; if (ntohs(sa_in->sin_port) == 0) sa_in->sin_port = htons(LDAP_PORT); idm->idm_state = STATE_DNS_DONE; break; case AF_INET6: sa_in6 = (struct sockaddr_in6 *)&h->ss; if (ntohs(sa_in6->sin6_port) == 0) sa_in6->sin6_port = htons(LDAP_PORT); idm->idm_state = STATE_DNS_DONE; break; default: fatalx("king bula sez: wrong AF in client_addr_init"); /* not reached */ } } return (0); } int client_addr_free(struct idm *idm) { struct ypldap_addr *h; while (!TAILQ_EMPTY(&idm->idm_addr)) { h = TAILQ_FIRST(&idm->idm_addr); TAILQ_REMOVE(&idm->idm_addr, h, next); free(h); } return (0); } void client_sig_handler(int sig, short event, void *p) { switch (sig) { case SIGINT: case SIGTERM: client_shutdown(); break; default: fatalx("unexpected signal"); } } void client_dispatch_dns(int fd, short events, void *p) { struct imsg imsg; u_int16_t dlen; u_char *data; struct ypldap_addr *h; int n, wait_cnt = 0; struct idm *idm; int shut = 0; struct env *env = p; struct imsgev *iev = env->sc_iev_dns; struct imsgbuf *ibuf = &iev->ibuf; if ((events & (EV_READ | EV_WRITE)) == 0) fatalx("unknown event"); if (events & EV_READ) { if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) fatal("imsg_read error"); if (n == 0) shut = 1; } if (events & EV_WRITE) { if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN) fatal("msgbuf_write"); if (n == 0) shut = 1; goto done; } for (;;) { if ((n = imsg_get(ibuf, &imsg)) == -1) fatal("client_dispatch_dns: imsg_get error"); if (n == 0) break; switch (imsg.hdr.type) { case IMSG_HOST_DNS: TAILQ_FOREACH(idm, &env->sc_idms, idm_entry) if (idm->idm_id == imsg.hdr.peerid) break; if (idm == NULL) { log_warnx("IMSG_HOST_DNS with invalid peerID"); break; } if (!TAILQ_EMPTY(&idm->idm_addr)) { log_warnx("IMSG_HOST_DNS but addrs set!"); break; } dlen = imsg.hdr.len - IMSG_HEADER_SIZE; if (dlen == 0) { /* no data -> temp error */ idm->idm_state = STATE_DNS_TEMPFAIL; break; } data = (u_char *)imsg.data; while (dlen >= sizeof(struct sockaddr_storage)) { if ((h = calloc(1, sizeof(*h))) == NULL) fatal(NULL); memcpy(&h->ss, data, sizeof(h->ss)); TAILQ_INSERT_HEAD(&idm->idm_addr, h, next); data += sizeof(h->ss); dlen -= sizeof(h->ss); } if (dlen != 0) fatalx("IMSG_HOST_DNS: dlen != 0"); client_addr_init(idm); break; default: break; } imsg_free(&imsg); } TAILQ_FOREACH(idm, &env->sc_idms, idm_entry) { if (client_try_idm(env, idm) == -1) idm->idm_state = STATE_LDAP_FAIL; if (idm->idm_state < STATE_LDAP_DONE) wait_cnt++; } if (wait_cnt == 0) imsg_compose_event(env->sc_iev, IMSG_END_UPDATE, 0, 0, -1, NULL, 0); done: if (!shut) imsg_event_add(iev); else { /* this pipe is dead, so remove the event handler */ event_del(&iev->ev); event_loopexit(NULL); } } void client_dispatch_parent(int fd, short events, void *p) { int n; int shut = 0; struct imsg imsg; struct env *env = p; struct imsgev *iev = env->sc_iev; struct imsgbuf *ibuf = &iev->ibuf; if ((events & (EV_READ | EV_WRITE)) == 0) fatalx("unknown event"); if (events & EV_READ) { if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) fatal("imsg_read error"); if (n == 0) shut = 1; } if (events & EV_WRITE) { if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN) fatal("msgbuf_write"); if (n == 0) shut = 1; goto done; } for (;;) { if ((n = imsg_get(ibuf, &imsg)) == -1) fatal("client_dispatch_parent: imsg_get error"); if (n == 0) break; switch (imsg.hdr.type) { case IMSG_CONF_START: { struct env params; if (env->sc_flags & F_CONFIGURING) { log_warnx("configuration already in progress"); break; } memcpy(¶ms, imsg.data, sizeof(params)); log_debug("configuration starting"); env->sc_flags |= F_CONFIGURING; purge_config(env); memcpy(&env->sc_conf_tv, ¶ms.sc_conf_tv, sizeof(env->sc_conf_tv)); env->sc_flags |= params.sc_flags; break; } case IMSG_CONF_IDM: { struct idm *idm; if (!(env->sc_flags & F_CONFIGURING)) break; if ((idm = calloc(1, sizeof(*idm))) == NULL) fatal(NULL); memcpy(idm, imsg.data, sizeof(*idm)); idm->idm_env = env; TAILQ_INSERT_TAIL(&env->sc_idms, idm, idm_entry); break; } case IMSG_CONF_END: env->sc_flags &= ~F_CONFIGURING; log_debug("applying configuration"); client_configure(env); break; default: log_debug("client_dispatch_parent: unexpect imsg %d", imsg.hdr.type); break; } imsg_free(&imsg); } done: if (!shut) imsg_event_add(iev); else { /* this pipe is dead, so remove the event handler */ event_del(&iev->ev); event_loopexit(NULL); } } void client_shutdown(void) { log_info("ldap client exiting"); _exit(0); } pid_t ldapclient(int pipe_main2client[2]) { pid_t pid, dns_pid; int pipe_dns[2]; struct passwd *pw; struct event ev_sigint; struct event ev_sigterm; struct env env; switch (pid = fork()) { case -1: fatal("cannot fork"); break; case 0: break; default: return (pid); } bzero(&env, sizeof(env)); TAILQ_INIT(&env.sc_idms); if ((pw = getpwnam(YPLDAP_USER)) == NULL) fatal("getpwnam"); if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe_dns) == -1) fatal("socketpair"); dns_pid = ypldap_dns(pipe_dns, pw); close(pipe_dns[1]); #ifndef DEBUG if (chroot(pw->pw_dir) == -1) fatal("chroot"); if (chdir("/") == -1) fatal("chdir"); #else #warning disabling chrooting in DEBUG mode #endif setproctitle("ldap client"); ypldap_process = PROC_CLIENT; #ifndef DEBUG if (setgroups(1, &pw->pw_gid) || setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) fatal("cannot drop privileges"); #else #warning disabling privilege revocation in DEBUG mode #endif event_init(); signal(SIGPIPE, SIG_IGN); signal_set(&ev_sigint, SIGINT, client_sig_handler, NULL); signal_set(&ev_sigterm, SIGTERM, client_sig_handler, NULL); signal_add(&ev_sigint, NULL); signal_add(&ev_sigterm, NULL); close(pipe_main2client[0]); if ((env.sc_iev = calloc(1, sizeof(*env.sc_iev))) == NULL) fatal(NULL); if ((env.sc_iev_dns = calloc(1, sizeof(*env.sc_iev_dns))) == NULL) fatal(NULL); env.sc_iev->events = EV_READ; env.sc_iev->data = &env; imsg_init(&env.sc_iev->ibuf, pipe_main2client[1]); env.sc_iev->handler = client_dispatch_parent; event_set(&env.sc_iev->ev, env.sc_iev->ibuf.fd, env.sc_iev->events, env.sc_iev->handler, &env); event_add(&env.sc_iev->ev, NULL); env.sc_iev_dns->events = EV_READ; env.sc_iev_dns->data = &env; imsg_init(&env.sc_iev_dns->ibuf, pipe_dns[0]); env.sc_iev_dns->handler = client_dispatch_dns; event_set(&env.sc_iev_dns->ev, env.sc_iev_dns->ibuf.fd, env.sc_iev_dns->events, env.sc_iev_dns->handler, &env); event_add(&env.sc_iev_dns->ev, NULL); event_dispatch(); client_shutdown(); return (0); } int client_build_req(struct idm *idm, struct idm_req *ir, struct aldap_message *m, int min_attr, int max_attr) { char **ldap_attrs; int i, k; bzero(ir, sizeof(*ir)); for (i = min_attr; i < max_attr; i++) { if (idm->idm_flags & F_FIXED_ATTR(i)) { if (strlcat(ir->ir_line, idm->idm_attrs[i], sizeof(ir->ir_line)) >= sizeof(ir->ir_line)) /* * entry yields a line > 1024, trash it. */ return (-1); if (i == ATTR_UID) { ir->ir_key.ik_uid = strtonum( idm->idm_attrs[i], 0, UID_MAX, NULL); } else if (i == ATTR_GR_GID) { ir->ir_key.ik_gid = strtonum( idm->idm_attrs[i], 0, GID_MAX, NULL); } } else if (idm->idm_list & F_LIST(i)) { aldap_match_attr(m, idm->idm_attrs[i], &ldap_attrs); for (k = 0; k >= 0 && ldap_attrs && ldap_attrs[k] != NULL; k++) { /* XXX: Fail when attributes have illegal characters e.g. ',' */ if (strlcat(ir->ir_line, ldap_attrs[k], sizeof(ir->ir_line)) >= sizeof(ir->ir_line)) continue; if (ldap_attrs[k+1] != NULL) if (strlcat(ir->ir_line, ",", sizeof(ir->ir_line)) >= sizeof(ir->ir_line)) { aldap_free_attr(ldap_attrs); return (-1); } } aldap_free_attr(ldap_attrs); } else { if (aldap_match_attr(m, idm->idm_attrs[i], &ldap_attrs) == -1) return (-1); - if (ldap_attrs[0] == NULL) - return (-1); if (strlcat(ir->ir_line, ldap_attrs[0], sizeof(ir->ir_line)) >= sizeof(ir->ir_line)) { aldap_free_attr(ldap_attrs); return (-1); } if (i == ATTR_UID) { ir->ir_key.ik_uid = strtonum( ldap_attrs[0], 0, UID_MAX, NULL); } else if (i == ATTR_GR_GID) { ir->ir_key.ik_uid = strtonum( ldap_attrs[0], 0, GID_MAX, NULL); } aldap_free_attr(ldap_attrs); } if (i + 1 != max_attr) if (strlcat(ir->ir_line, ":", sizeof(ir->ir_line)) >= sizeof(ir->ir_line)) return (-1); } return (0); } int client_search_idm(struct env *env, struct idm *idm, struct aldap *al, char **attrs, char *filter, int min_attr, int max_attr, enum imsg_type type) { struct idm_req ir; struct aldap_message *m; struct aldap_page_control *pg = NULL; const char *errstr; char *dn; dn = idm->idm_basedn; if (type == IMSG_GRP_ENTRY && idm->idm_groupdn[0] != '\0') dn = idm->idm_groupdn; do { if (aldap_search(al, dn, LDAP_SCOPE_SUBTREE, filter, attrs, 0, 0, 0, pg) == -1) { aldap_get_errno(al, &errstr); log_debug("%s", errstr); return (-1); } if (pg != NULL) { aldap_freepage(pg); pg = NULL; } while ((m = aldap_parse(al)) != NULL) { if (al->msgid != m->msgid) { goto fail; } if (m->message_type == LDAP_RES_SEARCH_RESULT) { if (m->page != NULL && m->page->cookie_len != 0) pg = m->page; else pg = NULL; aldap_freemsg(m); break; } if (m->message_type != LDAP_RES_SEARCH_ENTRY) { goto fail; } if (client_build_req(idm, &ir, m, min_attr, max_attr) == 0) imsg_compose_event(env->sc_iev, type, 0, 0, -1, &ir, sizeof(ir)); aldap_freemsg(m); } } while (pg != NULL); return (0); fail: aldap_freemsg(m); if (pg != NULL) { aldap_freepage(pg); } return (-1); } int client_try_idm(struct env *env, struct idm *idm) { const char *where; char *attrs[ATTR_MAX+1]; int i, j; struct aldap_message *m; struct aldap *al; where = "connect"; if ((al = client_aldap_open(&idm->idm_addr)) == NULL) return (-1); if (idm->idm_flags & F_NEEDAUTH) { where = "binding"; if (aldap_bind(al, idm->idm_binddn, idm->idm_bindcred) == -1) goto bad; where = "parsing"; if ((m = aldap_parse(al)) == NULL) goto bad; where = "verifying msgid"; if (al->msgid != m->msgid) { aldap_freemsg(m); goto bad; } aldap_freemsg(m); } bzero(attrs, sizeof(attrs)); for (i = 0, j = 0; i < ATTR_MAX; i++) { if (idm->idm_flags & F_FIXED_ATTR(i)) continue; attrs[j++] = idm->idm_attrs[i]; } attrs[j] = NULL; /* * build password line. */ where = "search"; log_debug("searching password entries"); if (client_search_idm(env, idm, al, attrs, idm->idm_filters[FILTER_USER], 0, ATTR_MAX, IMSG_PW_ENTRY) == -1) goto bad; bzero(attrs, sizeof(attrs)); for (i = ATTR_GR_MIN, j = 0; i < ATTR_GR_MAX; i++) { if (idm->idm_flags & F_FIXED_ATTR(i)) continue; attrs[j++] = idm->idm_attrs[i]; } attrs[j] = NULL; /* * build group line. */ where = "search"; log_debug("searching group entries"); if (client_search_idm(env, idm, al, attrs, idm->idm_filters[FILTER_GROUP], ATTR_GR_MIN, ATTR_GR_MAX, IMSG_GRP_ENTRY) == -1) goto bad; aldap_close(al); idm->idm_state = STATE_LDAP_DONE; return (0); bad: aldap_close(al); log_debug("directory %s errored out in %s", idm->idm_name, where); return (-1); } void client_periodic_update(int fd, short event, void *p) { struct env *env = p; struct idm *idm; int fail_cnt = 0; /* If LDAP isn't finished, notify the master process to trash the * update. */ TAILQ_FOREACH(idm, &env->sc_idms, idm_entry) { if (idm->idm_state < STATE_LDAP_DONE) fail_cnt++; idm->idm_state = STATE_NONE; client_addr_free(idm); } if (fail_cnt > 0) { log_debug("trash the update"); imsg_compose_event(env->sc_iev, IMSG_TRASH_UPDATE, 0, 0, -1, NULL, 0); } client_configure(env); } void client_configure(struct env *env) { struct timeval tv; struct idm *idm; u_int16_t dlen; log_debug("connecting to directories"); imsg_compose_event(env->sc_iev, IMSG_START_UPDATE, 0, 0, -1, NULL, 0); /* Start the DNS lookups */ TAILQ_FOREACH(idm, &env->sc_idms, idm_entry) { dlen = strlen(idm->idm_name) + 1; imsg_compose_event(env->sc_iev_dns, IMSG_HOST_DNS, idm->idm_id, 0, -1, idm->idm_name, dlen); } tv.tv_sec = env->sc_conf_tv.tv_sec; tv.tv_usec = env->sc_conf_tv.tv_usec; evtimer_set(&env->sc_conf_ev, client_periodic_update, env); evtimer_add(&env->sc_conf_ev, &tv); }