-# $OpenBSD: Makefile,v 1.8 2015/09/09 15:33:18 deraadt Exp $
+# $OpenBSD: Makefile,v 1.9 2017/05/30 09:33:31 jmatthew Exp $
PROG= ypldap
SRCS= parse.y ypldap.c log.c \
MAN= ypldap.8 ypldap.conf.5
-DPADD= ${LIBEVENT} ${LIBUTIL} ${LIBRPCSVC}
-LDADD= -levent -lutil -lrpcsvc
+DPADD= ${LIBTLS} ${LIBSSL} ${LIBCRYPTO} ${LIBEVENT} ${LIBUTIL} \
+ ${LIBRPCSVC}
+LDADD= -ltls -lssl -lcrypto -levent -lutil -lrpcsvc
CFLAGS+= -I${.CURDIR}
CFLAGS+= -Wall
CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes
-/* $Id: aldap.c,v 1.36 2017/03/27 04:46:47 jmatthew Exp $ */
-/* $OpenBSD: aldap.c,v 1.36 2017/03/27 04:46:47 jmatthew Exp $ */
+/* $Id: aldap.c,v 1.37 2017/05/30 09:33:31 jmatthew Exp $ */
+/* $OpenBSD: aldap.c,v 1.37 2017/05/30 09:33:31 jmatthew Exp $ */
/*
* Copyright (c) 2008 Alexander Schrijver <aschrijver@openbsd.org>
#include <stdlib.h>
#include <unistd.h>
+#include <event.h>
+
#include "aldap.h"
#if 0
char *parseval(char *, size_t);
int aldap_create_page_control(struct ber_element *,
int, struct aldap_page_control *);
+int aldap_send(struct aldap *,
+ struct ber_element *);
+unsigned long aldap_application(struct ber_element *);
#ifdef DEBUG
void ldap_debug_elements(struct ber_element *);
#define LDAP_DEBUG(x, y) do { } while (0)
#endif
+unsigned long
+aldap_application(struct ber_element *elm)
+{
+ return BER_TYPE_OCTETSTRING;
+}
+
int
aldap_close(struct aldap *al)
{
- if (close(al->ber.fd) == -1)
- return (-1);
+ if (al->fd != -1)
+ if (close(al->ber.fd) == -1)
+ return (-1);
ber_free(&al->ber);
+ evbuffer_free(al->buf);
free(al);
return (0);
if ((a = calloc(1, sizeof(*a))) == NULL)
return NULL;
- a->ber.fd = fd;
+ a->buf = evbuffer_new();
+ a->fd = fd;
+ a->ber.fd = -1;
+ ber_set_application(&a->ber, aldap_application);
return a;
}
+int
+aldap_tls(struct aldap *ldap, struct tls_config *cfg, const char *name)
+{
+ ldap->tls = tls_client();
+ if (ldap->tls == NULL) {
+ ldap->err = ALDAP_ERR_OPERATION_FAILED;
+ return (-1);
+ }
+
+ if (tls_configure(ldap->tls, cfg) == -1) {
+ ldap->err = ALDAP_ERR_TLS_ERROR;
+ return (-1);
+ }
+
+ if (tls_connect_socket(ldap->tls, ldap->fd, name) == -1) {
+ ldap->err = ALDAP_ERR_TLS_ERROR;
+ return (-1);
+ }
+
+ if (tls_handshake(ldap->tls) == -1) {
+ ldap->err = ALDAP_ERR_TLS_ERROR;
+ return (-1);
+ }
+
+ ldap->fd = -1;
+ return (0);
+}
+
+int
+aldap_send(struct aldap *ldap, struct ber_element *root)
+{
+ int error, wrote;
+ void *ptr;
+ char *data;
+ size_t len, done;
+
+ len = ber_calc_len(root);
+ error = ber_write_elements(&ldap->ber, root);
+ ber_free_elements(root);
+ if (error == -1)
+ return -1;
+
+ ber_get_writebuf(&ldap->ber, &ptr);
+ done = 0;
+ data = ptr;
+ while (len > 0) {
+ if (ldap->tls != NULL) {
+ wrote = tls_write(ldap->tls, data + done, len);
+ if (wrote == TLS_WANT_POLLIN ||
+ wrote == TLS_WANT_POLLOUT)
+ continue;
+ } else
+ wrote = write(ldap->fd, data + done, len);
+
+ if (wrote == -1)
+ return -1;
+
+ len -= wrote;
+ done += wrote;
+ }
+
+ return 0;
+}
+
+int
+aldap_req_starttls(struct aldap *ldap)
+{
+ struct ber_element *root = NULL, *ber;
+
+ if ((root = ber_add_sequence(NULL)) == NULL)
+ goto fail;
+
+ ber = ber_printf_elements(root, "d{tst", ++ldap->msgid, BER_CLASS_APP,
+ (unsigned long) LDAP_REQ_EXTENDED, LDAP_STARTTLS_OID,
+ BER_CLASS_CONTEXT, (unsigned long) 0);
+ if (ber == NULL) {
+ ldap->err = ALDAP_ERR_OPERATION_FAILED;
+ goto fail;
+ }
+
+ if (aldap_send(ldap, root) == -1)
+ goto fail;
+
+ return (ldap->msgid);
+fail:
+ if (root != NULL)
+ ber_free_elements(root);
+
+ ldap->err = ALDAP_ERR_OPERATION_FAILED;
+ return (-1);
+}
+
int
aldap_bind(struct aldap *ldap, char *binddn, char *bindcred)
{
struct ber_element *root = NULL, *elm;
- int error;
if (binddn == NULL)
binddn = "";
LDAP_DEBUG("aldap_bind", root);
- error = ber_write_elements(&ldap->ber, root);
- ber_free_elements(root);
- root = NULL;
- if (error == -1)
+ if (aldap_send(ldap, root) == -1) {
+ root = NULL;
goto fail;
-
+ }
return (ldap->msgid);
fail:
if (root != NULL)
aldap_unbind(struct aldap *ldap)
{
struct ber_element *root = NULL, *elm;
- int error;
if ((root = ber_add_sequence(NULL)) == NULL)
goto fail;
LDAP_DEBUG("aldap_unbind", root);
- error = ber_write_elements(&ldap->ber, root);
- ber_free_elements(root);
- root = NULL;
- if (error == -1)
+ if (aldap_send(ldap, root) == -1) {
+ root = NULL;
goto fail;
-
+ }
return (ldap->msgid);
fail:
if (root != NULL)
struct aldap_page_control *page)
{
struct ber_element *root = NULL, *ber, *c;
- int i, error;
+ int i;
if ((root = ber_add_sequence(NULL)) == NULL)
goto fail;
LDAP_DEBUG("aldap_search", root);
- error = ber_write_elements(&ldap->ber, root);
- ber_free_elements(root);
- root = NULL;
- if (error == -1) {
+ if (aldap_send(ldap, root) == -1) {
+ root = NULL;
ldap->err = ALDAP_ERR_OPERATION_FAILED;
goto fail;
}
long long msgid = 0;
struct aldap_message *m;
struct ber_element *a = NULL, *ep;
+ char rbuf[512];
+ int ret, retry;
if ((m = calloc(1, sizeof(struct aldap_message))) == NULL)
return NULL;
- if ((m->msg = ber_read_elements(&ldap->ber, NULL)) == NULL)
- goto parsefail;
+ retry = 0;
+ while (m->msg == NULL) {
+ if (retry || EVBUFFER_LENGTH(ldap->buf) == 0) {
+ if (ldap->tls) {
+ ret = tls_read(ldap->tls, rbuf, sizeof(rbuf));
+ if (ret == TLS_WANT_POLLIN ||
+ ret == TLS_WANT_POLLOUT)
+ continue;
+ } else
+ ret = read(ldap->fd, rbuf, sizeof(rbuf));
+
+ if (ret == -1) {
+ goto parsefail;
+ }
+
+ evbuffer_add(ldap->buf, rbuf, ret);
+ }
+
+ if (EVBUFFER_LENGTH(ldap->buf) > 0) {
+ ber_set_readbuf(&ldap->ber, EVBUFFER_DATA(ldap->buf),
+ EVBUFFER_LENGTH(ldap->buf));
+ errno = 0;
+ m->msg = ber_read_elements(&ldap->ber, NULL);
+ if (errno != 0 && errno != ECANCELED) {
+ goto parsefail;
+ }
+
+ retry = 1;
+ }
+ }
+
+ evbuffer_drain(ldap->buf, ldap->ber.br_rptr - ldap->ber.br_rbuf);
LDAP_DEBUG("message", m->msg);
if (ber_scanf_elements(m->protocol_op, "{e", &m->references) != 0)
goto parsefail;
break;
+ case LDAP_RES_EXTENDED:
+ if (ber_scanf_elements(m->protocol_op, "{E",
+ &m->body.res.rescode) != 0) {
+ goto parsefail;
+ }
+ break;
}
return m;
parsefail:
+ evbuffer_drain(ldap->buf, EVBUFFER_LENGTH(ldap->buf));
ldap->err = ALDAP_ERR_PARSER_ERROR;
aldap_freemsg(m);
return NULL;
case ALDAP_ERR_OPERATION_FAILED:
*estr = "operation failed";
break;
+ case ALDAP_ERR_TLS_ERROR:
+ *estr = tls_error(a->tls);
+ break;
default:
*estr = "unknown";
break;
-/* $Id: aldap.h,v 1.9 2012/04/30 21:40:03 jmatthew Exp $ */
-/* $OpenBSD: aldap.h,v 1.9 2012/04/30 21:40:03 jmatthew Exp $ */
+/* $Id: aldap.h,v 1.10 2017/05/30 09:33:31 jmatthew Exp $ */
+/* $OpenBSD: aldap.h,v 1.10 2017/05/30 09:33:31 jmatthew Exp $ */
/*
* Copyright (c) 2008 Alexander Schrijver <aschrijver@openbsd.org>
*/
#include <stdio.h>
+
+#include <tls.h>
+
#include "ber.h"
-#define LDAP_URL "ldap://"
-#define LDAP_PORT 389
-#define LDAP_PAGED_OID "1.2.840.113556.1.4.319"
+#define LDAP_URL "ldap://"
+#define LDAP_PORT 389
+#define LDAPS_PORT 636
+#define LDAP_PAGED_OID "1.2.840.113556.1.4.319"
+#define LDAP_STARTTLS_OID "1.3.6.1.4.1.1466.20037"
struct aldap {
#define ALDAP_ERR_SUCCESS 0
#define ALDAP_ERR_PARSER_ERROR 1
#define ALDAP_ERR_INVALID_FILTER 2
#define ALDAP_ERR_OPERATION_FAILED 3
+#define ALDAP_ERR_TLS_ERROR 4
u_int8_t err;
int msgid;
struct ber ber;
+
+ int fd;
+ struct tls *tls;
+
+ struct evbuffer *buf;
};
struct aldap_page_control {
LDAP_REQ_ABANDON_30 = 16,
LDAP_RES_SEARCH_REFERENCE = 19,
+
+ LDAP_REQ_EXTENDED = 23,
+ LDAP_RES_EXTENDED = 24
};
enum deref_aliases {
LDAP_FILT_SUBS_FIN = 2,
};
-struct aldap *aldap_init(int fd);
+struct aldap *aldap_init(int);
+int aldap_tls(struct aldap *, struct tls_config *,
+ const char *);
int aldap_close(struct aldap *);
struct aldap_message *aldap_parse(struct aldap *);
void aldap_freemsg(struct aldap_message *);
+int aldap_req_starttls(struct aldap *);
+
int aldap_bind(struct aldap *, char *, char *);
int aldap_unbind(struct aldap *);
int aldap_search(struct aldap *, char *, enum scope, char *, char **, int, int, int, struct aldap_page_control *);
-/* $OpenBSD: ldapclient.c,v 1.38 2017/01/20 12:39:36 benno Exp $ */
+/* $OpenBSD: ldapclient.c,v 1.39 2017/05/30 09:33:31 jmatthew Exp $ */
/*
* Copyright (c) 2008 Alexander Schrijver <aschrijver@openbsd.org>
warn("connect to %s port %s (%s) failed", hbuf, sbuf, "tcp");
close(fd);
+ fd = -1;
}
if (fd == -1)
{
struct sockaddr_in *sa_in;
struct sockaddr_in6 *sa_in6;
- struct ypldap_addr *h;
+ struct ypldap_addr *h;
+ int defport;
+
+ if (idm->idm_port != 0)
+ defport = idm->idm_port;
+ else if (idm->idm_flags & F_SSL)
+ defport = LDAPS_PORT;
+ else
+ defport = LDAP_PORT;
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);
+ sa_in->sin_port = htons(defport);
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);
+ sa_in6->sin6_port = htons(defport);
idm->idm_state = STATE_DNS_DONE;
break;
default:
if ((al = client_aldap_open(&idm->idm_addr)) == NULL)
return (-1);
+ if (idm->idm_flags & F_STARTTLS) {
+ log_debug("requesting starttls");
+ where = "starttls";
+ if (aldap_req_starttls(al) == -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;
+ }
+ where = "starttls result";
+ if (aldap_get_resultcode(m) != LDAP_SUCCESS) {
+ aldap_freemsg(m);
+ goto bad;
+ }
+ aldap_freemsg(m);
+ }
+
+ if (idm->idm_flags & (F_STARTTLS | F_SSL)) {
+ log_debug("starting tls");
+ where = "enabling tls";
+ if (aldap_tls(al, idm->idm_tls_config, idm->idm_name) < 0) {
+ const char *err;
+ aldap_get_errno(al, &err);
+ log_debug("tls failed: %s", err);
+ goto bad;
+ }
+ }
+
if (idm->idm_flags & F_NEEDAUTH) {
where = "binding";
if (aldap_bind(al, idm->idm_binddn, idm->idm_bindcred) == -1)
-/* $OpenBSD: parse.y,v 1.21 2017/01/05 13:53:10 krw Exp $ */
+/* $OpenBSD: parse.y,v 1.22 2017/05/30 09:33:31 jmatthew Exp $ */
/*
* Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
%token SERVER FILTER ATTRIBUTE BASEDN BINDDN GROUPDN BINDCRED MAPS CHANGE DOMAIN PROVIDE
%token USER GROUP TO EXPIRE HOME SHELL GECOS UID GID INTERVAL
%token PASSWD NAME FIXED LIST GROUPNAME GROUPPASSWD GROUPGID MAP
-%token INCLUDE DIRECTORY CLASS PORT ERROR GROUPMEMBERS
+%token INCLUDE DIRECTORY CLASS PORT ERROR GROUPMEMBERS LDAPS TLS CAFILE
%token <v.string> STRING
%token <v.number> NUMBER
%type <v.number> opcode attribute
-%type <v.string> port
+%type <v.number> port
+%type <v.number> ssl
%%
}
;
-port : /* empty */ { $$ = NULL; }
- | PORT STRING { $$ = $2; }
+port : PORT STRING {
+ struct servent *servent;
+
+ servent = getservbyname($2, "tcp");
+ if (servent == NULL) {
+ yyerror("port %s is invalid", $2);
+ free($2);
+ YYERROR;
+ }
+ $$ = ntohs(servent->s_port);
+ free($2);
+ }
+ | PORT NUMBER {
+ if ($2 <= 0 || $2 >= (int)USHRT_MAX) {
+ yyerror("invalid port: %lld", $2);
+ YYERROR;
+ }
+ $$ = $2;
+ }
+ | /* empty */ {
+ $$ = 0;
+ }
;
opcode : GROUP { $$ = 0; }
}
;
-directory : DIRECTORY STRING port {
+ssl : /* empty */ { $$ = 0; }
+ | LDAPS { $$ = F_SSL; }
+ | TLS { $$ = F_STARTTLS; }
+ ;
+
+directory : DIRECTORY STRING port ssl {
if ((idm = calloc(1, sizeof(*idm))) == NULL)
fatal(NULL);
idm->idm_id = conf->sc_maxid++;
free($2);
YYERROR;
}
-
free($2);
+
+ idm->idm_port = $3;
+
+ if ($4 != 0) {
+ if (tls_init()) {
+ yyerror("tls init failed");
+ YYERROR;
+ }
+
+ idm->idm_flags |= $4;
+ idm->idm_tls_config = tls_config_new();
+ if (idm->idm_tls_config == NULL) {
+ yyerror("tls config failed");
+ YYERROR;
+ }
+
+ if (tls_config_set_protocols(
+ idm->idm_tls_config,
+ TLS_PROTOCOLS_ALL) == -1) {
+ yyerror("tls set protocols failed: %s",
+ tls_config_error(
+ idm->idm_tls_config));
+ tls_config_free(idm->idm_tls_config);
+ idm->idm_tls_config = NULL;
+ YYERROR;
+ }
+ if (tls_config_set_ciphers(idm->idm_tls_config,
+ "compat") == -1) {
+ yyerror("tls set ciphers failed: %s",
+ tls_config_error(
+ idm->idm_tls_config));
+ tls_config_free(idm->idm_tls_config);
+ idm->idm_tls_config = NULL;
+ YYERROR;
+ }
+
+ if (tls_config_set_ca_file(idm->idm_tls_config,
+ conf->sc_cafile) == -1) {
+ yyerror("tls set CA bundle failed: %s",
+ tls_config_error(
+ idm->idm_tls_config));
+ tls_config_free(idm->idm_tls_config);
+ idm->idm_tls_config = NULL;
+ YYERROR;
+ }
+ }
+
} '{' optnl diropts '}' {
TAILQ_INSERT_TAIL(&conf->sc_idms, idm, idm_entry);
idm = NULL;
}
free($3);
}
+ | CAFILE STRING {
+ free(conf->sc_cafile);
+ conf->sc_cafile = $2;
+ }
;
diropts : diropts diropt nl
{ "basedn", BASEDN },
{ "bindcred", BINDCRED },
{ "binddn", BINDDN },
+ { "cafile", CAFILE },
{ "change", CHANGE },
{ "class", CLASS },
{ "directory", DIRECTORY },
{ "home", HOME },
{ "include", INCLUDE },
{ "interval", INTERVAL },
+ { "ldaps", LDAPS },
{ "list", LIST },
{ "map", MAP },
{ "maps", MAPS },
{ "provide", PROVIDE },
{ "server", SERVER },
{ "shell", SHELL },
+ { "tls", TLS },
{ "to", TO },
{ "uid", UID },
{ "user", USER },
TAILQ_INIT(&conf->sc_idms);
conf->sc_conf_tv.tv_sec = DEFAULT_INTERVAL;
conf->sc_conf_tv.tv_usec = 0;
+ conf->sc_cafile = strdup(YPLDAP_CERT_FILE);
+ if (conf->sc_cafile == NULL) {
+ log_warn("malloc");
+ return (-1);
+ }
errors = 0;
-.\" $OpenBSD: ypldap.conf.5,v 1.19 2012/04/30 11:28:25 jmatthew Exp $
+.\" $OpenBSD: ypldap.conf.5,v 1.20 2017/05/30 09:33:31 jmatthew Exp $
.\"
.\" Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
.\"
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
-.Dd $Mdocdate: April 30 2012 $
+.Dd $Mdocdate: May 30 2017 $
.Dt YPLDAP.CONF 5
.Os
.Sh NAME
.Pp
For example:
.Bd -literal -offset indent
-
fixed_gecos="Pulled from LDAP"
fixed attribute gecos $fixed_gecos
.Nm
The currently implemented maps are: passwd.byname, passwd.byuid,
group.byname, group.bygid.
+.It Ic cafile Ar filename
+Load CA certificates from the specified file to validate the server certificate.
+If not specified, CA certificates will be loaded from
+.Pa /etc/ssl/cert.pem .
.El
.Sh DIRECTORIES
Directories are used to describe the LDAP schema and help
and
.Xr group 5
lines.
-A directory declaration is of the following form:
-.Bd -literal -offset indent
-directory "some.host" {
- # directives
-}
+Each directory section consists of a declaration of the directory
+server name and a set of directives describing how entries from the
+directory are used to construct YP map entries.
+.Bl -tag -width Ds
+.It Ic directory Ar hostname Oo Ic port Ar port Oc Oo tls Oc Brq ...
+Defines a directory by hostname and optionally port number.
+If the
+.Ar tls
+argument is not specified, no transport-level security will be used.
+Valid options are:
+.Bl -tag -width Ds
+.It Ic tls
+Use STARTTLS to negotiate TLS, by default on port 389.
+.It Ic ldaps
+Connect with TLS enabled, by default on port 636.
+.El
+.El
.Ed
.Pp
Valid directives for directories are:
-/* $OpenBSD: ypldap.h,v 1.18 2017/01/20 12:39:36 benno Exp $ */
+/* $OpenBSD: ypldap.h,v 1.19 2017/05/30 09:33:31 jmatthew Exp $ */
/*
* Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
*/
#include <imsg.h>
+#include <tls.h>
#define YPLDAP_USER "_ypldap"
#define YPLDAP_CONF_FILE "/etc/ypldap.conf"
+#define YPLDAP_CERT_FILE "/etc/ssl/cert.pem"
#define DEFAULT_INTERVAL 600
#define LINE_WIDTH 1024
#define FILTER_WIDTH 128
#define F_SSL 0x00100000
#define F_CONFIGURING 0x00200000
#define F_NEEDAUTH 0x00400000
+#define F_STARTTLS 0x00800000
#define F_FIXED_ATTR(n) (1<<n)
#define F_LIST(n) (1<<n)
enum client_state idm_state;
#define ATTR_GR_MAX 14
char idm_attrs[14][ATTR_WIDTH];
struct env *idm_env;
- struct event idm_ev;
-#ifdef SSL
- struct ssl *idm_ssl;
-#endif
+ struct tls_config *idm_tls_config;
};
struct idm_req {
char sc_domainname[HOST_NAME_MAX+1];
struct timeval sc_conf_tv;
struct event sc_conf_ev;
+ char *sc_cafile;
TAILQ_HEAD(idm_list, idm) sc_idms;
struct imsgev *sc_iev;
struct imsgev *sc_iev_dns;