xref: /openbsd-src/libexec/login_ldap/util.c (revision 93f6aaeac35d17b9ef4d669893f6a2ad3d4defb7)
1f95714a4Smartijn /*
2*93f6aaeaSjsg  * $OpenBSD: util.c,v 1.5 2023/04/19 12:34:23 jsg Exp $
3f95714a4Smartijn  * Copyright (c) 2002 Institute for Open Systems Technology Australia (IFOST)
4f95714a4Smartijn  * Copyright (c) 2007 Michael Erdely <merdely@openbsd.org>
5f95714a4Smartijn  * Copyright (c) 2019 Martijn van Duren <martijn@openbsd.org>
6f95714a4Smartijn  *
7f95714a4Smartijn  * All rights reserved.
8f95714a4Smartijn  *
9f95714a4Smartijn  * Redistribution and use in source and binary forms, with or without
10f95714a4Smartijn  * modification, are permitted provided that the following conditions
11f95714a4Smartijn  * are met:
12f95714a4Smartijn  * 1. Redistributions of source code must retain the above copyright
13f95714a4Smartijn  *    notice, this list of conditions and the following disclaimer.
14f95714a4Smartijn  * 2. Redistributions in binary form must reproduce the above copyright
15f95714a4Smartijn  *    notice, this list of conditions and the following disclaimer in the
16f95714a4Smartijn  *    documentation and/or other materials provided with the distribution.
17f95714a4Smartijn  * 3. The name of the author may not be used to endorse or promote products
18f95714a4Smartijn  *    derived from this software without specific prior written permission.
19f95714a4Smartijn  *
20f95714a4Smartijn  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
21f95714a4Smartijn  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
22f95714a4Smartijn  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
23f95714a4Smartijn  * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24f95714a4Smartijn  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25f95714a4Smartijn  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
26f95714a4Smartijn  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27f95714a4Smartijn  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
28f95714a4Smartijn  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
29f95714a4Smartijn  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30f95714a4Smartijn  */
3106971c39Sderaadt 
32f95714a4Smartijn #include <sys/socket.h>
33f95714a4Smartijn #include <sys/time.h>
34f95714a4Smartijn #include <sys/types.h>
35f95714a4Smartijn #include <sys/stat.h>
36f95714a4Smartijn #include <sys/un.h>
37f95714a4Smartijn #include <netinet/in.h>
38f95714a4Smartijn 
39f95714a4Smartijn #include <ctype.h>
40f95714a4Smartijn #include <grp.h>
41f95714a4Smartijn #include <unistd.h>
42f95714a4Smartijn #include <stdio.h>
43f95714a4Smartijn #include <stdarg.h>
44f95714a4Smartijn #include <stdlib.h>
45f95714a4Smartijn #include <string.h>
46f95714a4Smartijn #include <limits.h>
47f95714a4Smartijn #include <errno.h>
48f95714a4Smartijn #include <syslog.h>
49f95714a4Smartijn #include <tls.h>
50f95714a4Smartijn #include <netdb.h>
51f95714a4Smartijn #include <login_cap.h>
52f95714a4Smartijn 
53f95714a4Smartijn #include "aldap.h"
54f95714a4Smartijn #include "login_ldap.h"
55f95714a4Smartijn 
56f95714a4Smartijn int debug = 0;
57f95714a4Smartijn 
58f95714a4Smartijn static int getscope(char *);
59f95714a4Smartijn 
60f95714a4Smartijn void
dlog(int d,char * fmt,...)61f95714a4Smartijn dlog(int d, char *fmt, ...)
62f95714a4Smartijn {
63f95714a4Smartijn 	va_list ap;
64f95714a4Smartijn 
65f95714a4Smartijn 	/*
666d9ddefdSjmc 	 * if debugging is on, print everything to stderr
67f95714a4Smartijn 	 * otherwise, syslog it if d = 0. messing with
68f95714a4Smartijn 	 * newlines means there wont be newlines in stuff
69f95714a4Smartijn 	 * that goes to syslog.
70f95714a4Smartijn 	 */
71f95714a4Smartijn 
72f95714a4Smartijn 	va_start(ap, fmt);
73f95714a4Smartijn 	if (debug) {
74f95714a4Smartijn 		vfprintf(stderr, fmt, ap);
75f95714a4Smartijn 		fputc('\n', stderr);
76f95714a4Smartijn 	} else if (d == 0)
77f95714a4Smartijn 		vsyslog(LOG_WARNING, fmt, ap);
78f95714a4Smartijn 
79f95714a4Smartijn 	va_end(ap);
80f95714a4Smartijn }
81f95714a4Smartijn 
82f95714a4Smartijn const char *
ldap_resultcode(enum result_code code)83f95714a4Smartijn ldap_resultcode(enum result_code code)
84f95714a4Smartijn {
85f95714a4Smartijn #define CODE(_X)	case _X:return (#_X)
86f95714a4Smartijn 	switch (code) {
87f95714a4Smartijn 	CODE(LDAP_SUCCESS);
88f95714a4Smartijn 	CODE(LDAP_OPERATIONS_ERROR);
89f95714a4Smartijn 	CODE(LDAP_PROTOCOL_ERROR);
90f95714a4Smartijn 	CODE(LDAP_TIMELIMIT_EXCEEDED);
91f95714a4Smartijn 	CODE(LDAP_SIZELIMIT_EXCEEDED);
92f95714a4Smartijn 	CODE(LDAP_COMPARE_FALSE);
93f95714a4Smartijn 	CODE(LDAP_COMPARE_TRUE);
94f95714a4Smartijn 	CODE(LDAP_STRONG_AUTH_NOT_SUPPORTED);
95f95714a4Smartijn 	CODE(LDAP_STRONG_AUTH_REQUIRED);
96f95714a4Smartijn 	CODE(LDAP_REFERRAL);
97f95714a4Smartijn 	CODE(LDAP_ADMINLIMIT_EXCEEDED);
98f95714a4Smartijn 	CODE(LDAP_UNAVAILABLE_CRITICAL_EXTENSION);
99f95714a4Smartijn 	CODE(LDAP_CONFIDENTIALITY_REQUIRED);
100f95714a4Smartijn 	CODE(LDAP_SASL_BIND_IN_PROGRESS);
101f95714a4Smartijn 	CODE(LDAP_NO_SUCH_ATTRIBUTE);
102f95714a4Smartijn 	CODE(LDAP_UNDEFINED_TYPE);
103f95714a4Smartijn 	CODE(LDAP_INAPPROPRIATE_MATCHING);
104f95714a4Smartijn 	CODE(LDAP_CONSTRAINT_VIOLATION);
105f95714a4Smartijn 	CODE(LDAP_TYPE_OR_VALUE_EXISTS);
106f95714a4Smartijn 	CODE(LDAP_INVALID_SYNTAX);
107f95714a4Smartijn 	CODE(LDAP_NO_SUCH_OBJECT);
108f95714a4Smartijn 	CODE(LDAP_ALIAS_PROBLEM);
109f95714a4Smartijn 	CODE(LDAP_INVALID_DN_SYNTAX);
110f95714a4Smartijn 	CODE(LDAP_ALIAS_DEREF_PROBLEM);
111f95714a4Smartijn 	CODE(LDAP_INAPPROPRIATE_AUTH);
112f95714a4Smartijn 	CODE(LDAP_INVALID_CREDENTIALS);
113f95714a4Smartijn 	CODE(LDAP_INSUFFICIENT_ACCESS);
114f95714a4Smartijn 	CODE(LDAP_BUSY);
115f95714a4Smartijn 	CODE(LDAP_UNAVAILABLE);
116f95714a4Smartijn 	CODE(LDAP_UNWILLING_TO_PERFORM);
117f95714a4Smartijn 	CODE(LDAP_LOOP_DETECT);
118f95714a4Smartijn 	CODE(LDAP_NAMING_VIOLATION);
119f95714a4Smartijn 	CODE(LDAP_OBJECT_CLASS_VIOLATION);
120f95714a4Smartijn 	CODE(LDAP_NOT_ALLOWED_ON_NONLEAF);
121f95714a4Smartijn 	CODE(LDAP_NOT_ALLOWED_ON_RDN);
122f95714a4Smartijn 	CODE(LDAP_ALREADY_EXISTS);
123f95714a4Smartijn 	CODE(LDAP_NO_OBJECT_CLASS_MODS);
124f95714a4Smartijn 	CODE(LDAP_AFFECTS_MULTIPLE_DSAS);
125f95714a4Smartijn 	CODE(LDAP_OTHER);
126f95714a4Smartijn 	}
127f95714a4Smartijn 
128f95714a4Smartijn 	return ("UNKNOWN_ERROR");
129f95714a4Smartijn };
130f95714a4Smartijn 
131f95714a4Smartijn 
132f95714a4Smartijn static int
parse_server_line(char * buf,struct aldap_url * s)133f95714a4Smartijn parse_server_line(char *buf, struct aldap_url *s)
134f95714a4Smartijn {
135f95714a4Smartijn 	/**
136f95714a4Smartijn 	 * host=[<protocol>://]<hostname>[:port]
137f95714a4Smartijn 	 *
138f95714a4Smartijn 	 * must have a hostname
139f95714a4Smartijn 	 * protocol can be "ldap", "ldaps", "ldap+tls" or "ldapi"
140f95714a4Smartijn 	 * for ldap and ldap+tls, port defaults to 389
141f95714a4Smartijn 	 * for ldaps, port defaults to 636
142f95714a4Smartijn 	 */
143f95714a4Smartijn 
144f95714a4Smartijn 	if (buf == NULL) {
145f95714a4Smartijn 		dlog(1, "%s got NULL buf!", __func__);
146f95714a4Smartijn 		return 0;
147f95714a4Smartijn 	}
148f95714a4Smartijn 
149f95714a4Smartijn 	dlog(1, "parse_server_line buf = %s", buf);
150f95714a4Smartijn 
151f95714a4Smartijn 	memset(s, 0, sizeof(*s));
152f95714a4Smartijn 
153f95714a4Smartijn 	if (aldap_parse_url(buf, s) == -1) {
154f95714a4Smartijn 		dlog(0, "failed to parse host %s", buf);
155f95714a4Smartijn 		return 0;
156f95714a4Smartijn 	}
157f95714a4Smartijn 
158f95714a4Smartijn 	if (s->protocol == -1)
159f95714a4Smartijn 		s->protocol = LDAP;
160f95714a4Smartijn 	if (s->protocol != LDAPI && s->port == 0) {
161f95714a4Smartijn 		if (s->protocol == LDAPS)
162f95714a4Smartijn 			s->port = 636;
163f95714a4Smartijn 		else
164f95714a4Smartijn 			s->port = 389;
165f95714a4Smartijn 	}
166f95714a4Smartijn 
167f95714a4Smartijn 	return 1;
168f95714a4Smartijn }
169f95714a4Smartijn 
170f95714a4Smartijn int
parse_conf(struct auth_ctx * ctx,const char * path)171f95714a4Smartijn parse_conf(struct auth_ctx *ctx, const char *path)
172f95714a4Smartijn {
173f95714a4Smartijn 	FILE *cf;
174f95714a4Smartijn 	struct stat sb;
175f95714a4Smartijn 	struct group *grp;
176f95714a4Smartijn 	struct aldap_urlq *url;
177f95714a4Smartijn 	char *buf = NULL, *key, *value, *tail;
178f95714a4Smartijn 	const char *errstr;
179f95714a4Smartijn 	size_t buflen = 0;
180f95714a4Smartijn 	ssize_t linelen;
181f95714a4Smartijn 
182f95714a4Smartijn 	dlog(1, "Parsing config file '%s'", path);
183f95714a4Smartijn 
184f95714a4Smartijn 	if ((cf = fopen(path, "r")) == NULL) {
185f95714a4Smartijn 		dlog(0, "Can't open config file: %s", strerror(errno));
186f95714a4Smartijn 		return 0;
187f95714a4Smartijn 	}
188f95714a4Smartijn 	if (fstat(fileno(cf), &sb) == -1) {
189f95714a4Smartijn 		dlog(0, "Can't stat config file: %s", strerror(errno));
190f95714a4Smartijn 		return 0;
191f95714a4Smartijn 	}
192f95714a4Smartijn 	if ((grp = getgrnam("auth")) == NULL) {
193f95714a4Smartijn 		dlog(0, "Can't find group auth");
194f95714a4Smartijn 		return 0;
195f95714a4Smartijn 	}
196f95714a4Smartijn 	if (sb.st_uid != 0 ||
197f95714a4Smartijn 	    sb.st_gid != grp->gr_gid ||
198f95714a4Smartijn 	    (sb.st_mode & S_IRWXU) != (S_IRUSR | S_IWUSR) ||
199f95714a4Smartijn 	    (sb.st_mode & S_IRWXG) != S_IRGRP ||
200f95714a4Smartijn 	    (sb.st_mode & S_IRWXO) != 0) {
201f95714a4Smartijn 		dlog(0, "Wrong permissions for config file");
202f95714a4Smartijn 		return 0;
203f95714a4Smartijn 	}
204f95714a4Smartijn 
205f95714a4Smartijn 	/* We need a default scope */
206f95714a4Smartijn 	ctx->gscope = ctx->scope = getscope(NULL);
207f95714a4Smartijn 
208f95714a4Smartijn 	while ((linelen = getline(&buf, &buflen, cf)) != -1) {
209f95714a4Smartijn 		if (buf[linelen - 1] == '\n')
210f95714a4Smartijn 			buf[linelen -1] = '\0';
211f95714a4Smartijn 		/* Allow leading spaces */
212f95714a4Smartijn 		for (key = buf; key[0] != '\0' && isspace(key[0]); key++)
213f95714a4Smartijn 			continue;
214f95714a4Smartijn 		/* Comment or white lines */
215f95714a4Smartijn 		if (key[0] == '#' || key[0] == '\0')
216f95714a4Smartijn 			continue;
217f95714a4Smartijn 		if ((tail = value = strchr(key, '=')) == NULL) {
218f95714a4Smartijn 			dlog(0, "Missing value for option '%s'", key);
219f95714a4Smartijn 			return 0;
220f95714a4Smartijn 		}
221f95714a4Smartijn 		value++;
222f95714a4Smartijn 		/* Don't fail over trailing key spaces */
223f95714a4Smartijn 		for (tail--; isspace(tail[0]); tail--)
224f95714a4Smartijn 			continue;
225f95714a4Smartijn 		tail[1] = '\0';
226f95714a4Smartijn 		if (strcmp(key, "host") == 0) {
227f95714a4Smartijn 			if ((url = calloc(1, sizeof(*url))) == NULL) {
228f95714a4Smartijn 				dlog(0, "Failed to add %s: %s", value,
229f95714a4Smartijn 				    strerror(errno));
230f95714a4Smartijn 				continue;
231f95714a4Smartijn 			}
232f95714a4Smartijn 			if (parse_server_line(value, &(url->s)) == 0) {
233f95714a4Smartijn 				free(url);
234f95714a4Smartijn 				return 0;
235f95714a4Smartijn 			}
236f95714a4Smartijn 			TAILQ_INSERT_TAIL(&(ctx->s), url, entries);
237f95714a4Smartijn 		} else if (strcmp(key, "basedn") == 0) {
238f95714a4Smartijn 			free(ctx->basedn);
239f95714a4Smartijn 			if ((ctx->basedn = strdup(value)) == NULL) {
240f95714a4Smartijn 				dlog(0, "%s", strerror(errno));
241f95714a4Smartijn 				return 0;
242f95714a4Smartijn 			}
243f95714a4Smartijn 		} else if (strcmp(key, "binddn") == 0) {
244f95714a4Smartijn 			free(ctx->binddn);
245f95714a4Smartijn 			if ((ctx->binddn = parse_filter(ctx, value)) == NULL)
246f95714a4Smartijn 				return 0;
247f95714a4Smartijn 		} else if (strcmp(key, "bindpw") == 0) {
248f95714a4Smartijn 			free(ctx->bindpw);
249f95714a4Smartijn 			if ((ctx->bindpw = strdup(value)) == NULL) {
250f95714a4Smartijn 				dlog(0, "%s", strerror(errno));
251f95714a4Smartijn 				return 0;
252f95714a4Smartijn 			}
253f95714a4Smartijn 		} else if (strcmp(key, "timeout") == 0) {
254f95714a4Smartijn 			ctx->timeout = strtonum(value, 0, INT_MAX, &errstr);
255f95714a4Smartijn 			if (ctx->timeout == 0 && errstr != NULL) {
256f95714a4Smartijn 				dlog(0, "timeout %s", errstr);
257f95714a4Smartijn 				return 0;
258f95714a4Smartijn 			}
259f95714a4Smartijn 		} else if (strcmp(key, "filter") == 0) {
260f95714a4Smartijn 			free(ctx->filter);
261f95714a4Smartijn 			if ((ctx->filter = parse_filter(ctx, value)) == NULL)
262f95714a4Smartijn 				return 0;
263f95714a4Smartijn 		} else if (strcmp(key, "scope") == 0) {
264f95714a4Smartijn 			if ((ctx->scope = getscope(value)) == -1)
265f95714a4Smartijn 				return 0;
266f95714a4Smartijn 		} else if (strcmp(key, "cacert") == 0) {
267f95714a4Smartijn 			free(ctx->cacert);
268f95714a4Smartijn 			if ((ctx->cacert = strdup(value)) == NULL) {
269f95714a4Smartijn 				dlog(0, "%s", strerror(errno));
270f95714a4Smartijn 				return 0;
271f95714a4Smartijn 			}
272f95714a4Smartijn 		} else if (strcmp(key, "cacertdir") == 0) {
273f95714a4Smartijn 			free(ctx->cacertdir);
274f95714a4Smartijn 			if ((ctx->cacertdir = strdup(value)) == NULL) {
275f95714a4Smartijn 				dlog(0, "%s", strerror(errno));
276f95714a4Smartijn 				return 0;
277f95714a4Smartijn 			}
278f95714a4Smartijn 		} else if (strcmp(key, "gbasedn") == 0) {
279f95714a4Smartijn 			free(ctx->gbasedn);
280f95714a4Smartijn 			if ((ctx->gbasedn = strdup(value)) == NULL) {
281f95714a4Smartijn 				dlog(0, "%s", strerror(errno));
282f95714a4Smartijn 				return 0;
283f95714a4Smartijn 			}
284f95714a4Smartijn 		} else if (strcmp(key, "gfilter") == 0) {
285f95714a4Smartijn 			free(ctx->gfilter);
286f95714a4Smartijn 			if ((ctx->gfilter = strdup(value)) == NULL) {
287f95714a4Smartijn 				dlog(0, "%s", strerror(errno));
288f95714a4Smartijn 				return 0;
289f95714a4Smartijn 			}
290f95714a4Smartijn 		} else if (strcmp(key, "gscope") == 0) {
291f95714a4Smartijn 			if ((ctx->scope = getscope(value)) == -1)
292f95714a4Smartijn 				return 0;
293f95714a4Smartijn 		} else {
294f95714a4Smartijn 			dlog(0, "Unknown option '%s'", key);
295f95714a4Smartijn 			return 0;
296f95714a4Smartijn 		}
297f95714a4Smartijn 	}
298f95714a4Smartijn 	if (ferror(cf)) {
299f95714a4Smartijn 		dlog(0, "Can't read config file: %s", strerror(errno));
300f95714a4Smartijn 		return 0;
301f95714a4Smartijn 	}
302f95714a4Smartijn 	if (TAILQ_EMPTY(&(ctx->s))) {
303f95714a4Smartijn 		dlog(0, "Missing host");
304f95714a4Smartijn 		return 0;
305f95714a4Smartijn 	}
306f95714a4Smartijn 	if (ctx->basedn == NULL && ctx->binddn == NULL) {
307f95714a4Smartijn 		dlog(0, "Missing basedn or binddn");
308f95714a4Smartijn 		return 0;
309f95714a4Smartijn 	}
310f95714a4Smartijn 	return 1;
311f95714a4Smartijn }
312f95714a4Smartijn 
313f95714a4Smartijn int
do_conn(struct auth_ctx * ctx,struct aldap_url * url)314f95714a4Smartijn do_conn(struct auth_ctx *ctx, struct aldap_url *url)
315f95714a4Smartijn {
316f95714a4Smartijn 	struct addrinfo		 ai, *res, *res0;
317f95714a4Smartijn 	struct sockaddr_un	 un;
318f95714a4Smartijn 	struct aldap_message	*m;
319f95714a4Smartijn 	struct tls_config	*tls_config;
320f95714a4Smartijn 	const char		*errstr;
321f95714a4Smartijn 	char			 port[6];
322f95714a4Smartijn 	int			 fd, code;
323f95714a4Smartijn 
324f95714a4Smartijn 	dlog(1, "host %s, port %d", url->host, url->port);
325f95714a4Smartijn 
326f95714a4Smartijn 	if (url->protocol == LDAPI) {
327f95714a4Smartijn 		memset(&un, 0, sizeof(un));
328f95714a4Smartijn 		un.sun_family = AF_UNIX;
329f95714a4Smartijn 		if (strlcpy(un.sun_path, url->host,
330f95714a4Smartijn 		    sizeof(un.sun_path)) >= sizeof(un.sun_path)) {
331f95714a4Smartijn 			dlog(0, "socket '%s' too long", url->host);
332f95714a4Smartijn 			return 0;
333f95714a4Smartijn 		}
334f95714a4Smartijn 		if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1 ||
335f95714a4Smartijn 		    connect(fd, (struct sockaddr *)&un, sizeof(un)) == -1) {
336f95714a4Smartijn 			dlog(0, "can't create socket '%s'", url->host);
337f95714a4Smartijn 			return 0;
338f95714a4Smartijn 		}
339f95714a4Smartijn 	} else {
340f95714a4Smartijn 		memset(&ai, 0, sizeof(ai));
341f95714a4Smartijn 		ai.ai_family = AF_UNSPEC;
342f95714a4Smartijn 		ai.ai_socktype = SOCK_STREAM;
343f95714a4Smartijn 		ai.ai_protocol = IPPROTO_TCP;
344f95714a4Smartijn 		(void)snprintf(port, sizeof(port), "%u", url->port);
345f95714a4Smartijn 		if ((code = getaddrinfo(url->host, port,
346f95714a4Smartijn 		    &ai, &res0)) != 0) {
347f95714a4Smartijn 			dlog(0, "%s", gai_strerror(code));
348f95714a4Smartijn 			return 0;
349f95714a4Smartijn 		}
350f95714a4Smartijn 		for (res = res0; res; res = res->ai_next, fd = -1) {
351f95714a4Smartijn 			if ((fd = socket(res->ai_family, res->ai_socktype,
352f95714a4Smartijn 			    res->ai_protocol)) == -1)
353f95714a4Smartijn 				continue;
354f95714a4Smartijn 
355f95714a4Smartijn 			if (connect(fd, res->ai_addr, res->ai_addrlen) >= 0)
356f95714a4Smartijn 				break;
357f95714a4Smartijn 
358f95714a4Smartijn 			close(fd);
359f95714a4Smartijn 		}
360f95714a4Smartijn 		freeaddrinfo(res0);
361f95714a4Smartijn 		if (fd == -1)
362f95714a4Smartijn 			return 0;
363f95714a4Smartijn 	}
364f95714a4Smartijn 
365f95714a4Smartijn 	ctx->ld = aldap_init(fd);
366f95714a4Smartijn 	if (ctx->ld == NULL) {
367f95714a4Smartijn 		dlog(0, "aldap_open(%s:%hd) failed", url->host, url->port);
368f95714a4Smartijn 		return 0;
369f95714a4Smartijn 	}
370f95714a4Smartijn 
371f95714a4Smartijn 	dlog(1, "connect success!");
372f95714a4Smartijn 
373f95714a4Smartijn 	if (url->protocol == LDAPTLS) {
374f95714a4Smartijn 		dlog(1, "starttls!");
375f95714a4Smartijn 		if (aldap_req_starttls(ctx->ld) == -1) {
376f95714a4Smartijn 			dlog(0, "failed to request STARTTLS");
377f95714a4Smartijn 			goto fail;
378f95714a4Smartijn 		}
379f95714a4Smartijn 
380f95714a4Smartijn 		if ((m = aldap_parse(ctx->ld)) == NULL) {
381f95714a4Smartijn 			dlog(0, "failed to parse STARTTLS response");
382f95714a4Smartijn 			goto fail;
383f95714a4Smartijn 		}
384f95714a4Smartijn 
385f95714a4Smartijn 		if (ctx->ld->msgid != m->msgid ||
386f95714a4Smartijn 		    (code = aldap_get_resultcode(m)) != LDAP_SUCCESS) {
387f95714a4Smartijn 			dlog(0, "STARTTLS failed: %s(%d)",
388f95714a4Smartijn 			    ldap_resultcode(code), code);
389f95714a4Smartijn 			aldap_freemsg(m);
390f95714a4Smartijn 			goto fail;
391f95714a4Smartijn 		}
392f95714a4Smartijn 		aldap_freemsg(m);
393f95714a4Smartijn 	}
394f95714a4Smartijn 	if (url->protocol == LDAPTLS || url->protocol == LDAPS) {
395f95714a4Smartijn 		dlog(1, "%s: starting TLS", __func__);
396f95714a4Smartijn 
397f95714a4Smartijn 		if ((tls_config = tls_config_new()) == NULL) {
398f95714a4Smartijn 			dlog(0, "TLS config failed");
399f95714a4Smartijn 			goto fail;
400f95714a4Smartijn 		}
401f95714a4Smartijn 
402f95714a4Smartijn 		if (ctx->cacert != NULL &&
403f95714a4Smartijn 		    tls_config_set_ca_file(tls_config, ctx->cacert) == -1) {
404f95714a4Smartijn 			dlog(0, "Failed to set ca file %s", ctx->cacert);
405f95714a4Smartijn 			goto fail;
406f95714a4Smartijn 		}
407f95714a4Smartijn 		if (ctx->cacertdir != NULL &&
408f95714a4Smartijn 		    tls_config_set_ca_path(tls_config, ctx->cacertdir) == -1) {
409f95714a4Smartijn 			dlog(0, "Failed to set ca dir %s", ctx->cacertdir);
410f95714a4Smartijn 			goto fail;
411f95714a4Smartijn 		}
412f95714a4Smartijn 
413f95714a4Smartijn 		if (aldap_tls(ctx->ld, tls_config, url->host) < 0) {
414f95714a4Smartijn 			aldap_get_errno(ctx->ld, &errstr);
415f95714a4Smartijn 			dlog(0, "TLS failed: %s", errstr);
416f95714a4Smartijn 			goto fail;
417f95714a4Smartijn 		}
418f95714a4Smartijn 	}
419f95714a4Smartijn 	return 1;
420f95714a4Smartijn fail:
421f95714a4Smartijn 	aldap_close(ctx->ld);
422f95714a4Smartijn 	return 0;
423f95714a4Smartijn }
424f95714a4Smartijn 
425f95714a4Smartijn int
conn(struct auth_ctx * ctx)426f95714a4Smartijn conn(struct auth_ctx *ctx)
427f95714a4Smartijn {
428f95714a4Smartijn 	struct aldap_urlq *url;
429f95714a4Smartijn 
430f95714a4Smartijn 	TAILQ_FOREACH(url, &(ctx->s), entries) {
431f95714a4Smartijn 		if (do_conn(ctx, &(url->s)))
432f95714a4Smartijn 			return 1;
433f95714a4Smartijn 	}
434f95714a4Smartijn 
435f95714a4Smartijn 	/* all the urls have failed */
436f95714a4Smartijn 	return 0;
437f95714a4Smartijn }
438f95714a4Smartijn 
439f95714a4Smartijn static int
getscope(char * scope)440f95714a4Smartijn getscope(char *scope)
441f95714a4Smartijn {
442f95714a4Smartijn 	if (scope == NULL || scope[0] == '\0')
443f95714a4Smartijn 		return LDAP_SCOPE_SUBTREE;
444f95714a4Smartijn 
445f95714a4Smartijn 	if (strcmp(scope, "base") == 0)
446f95714a4Smartijn 		return LDAP_SCOPE_BASE;
447f95714a4Smartijn 	else if (strcmp(scope, "one") == 0)
448f95714a4Smartijn 		return LDAP_SCOPE_ONELEVEL;
449f95714a4Smartijn 	else if (strcmp(scope, "sub") == 0)
450f95714a4Smartijn 		return LDAP_SCOPE_SUBTREE;
451f95714a4Smartijn 
452f95714a4Smartijn 	dlog(0, "Invalid scope");
453f95714a4Smartijn 	return -1;
454f95714a4Smartijn }
455f95714a4Smartijn 
456f95714a4Smartijn /*
457f95714a4Smartijn  * Convert format specifiers from the filter in login.conf to their
458f95714a4Smartijn  * real values. return the new filter in the filter argument.
459f95714a4Smartijn  */
460f95714a4Smartijn char *
parse_filter(struct auth_ctx * ctx,const char * str)461f95714a4Smartijn parse_filter(struct auth_ctx *ctx, const char *str)
462f95714a4Smartijn {
463f95714a4Smartijn 	char tmp[PATH_MAX];
46406971c39Sderaadt 	char hostname[HOST_NAME_MAX+1];
465f95714a4Smartijn 	const char *p;
466f95714a4Smartijn 	char *q;
467f95714a4Smartijn 
468f95714a4Smartijn 	if (str == NULL)
469f95714a4Smartijn 		return NULL;
470f95714a4Smartijn 
471f95714a4Smartijn 	/*
472f95714a4Smartijn 	 * copy over from str to q, if we hit a %, substitute the real value,
473f95714a4Smartijn 	 * if we hit a NULL, its the end of the filter string
474f95714a4Smartijn 	 */
475f95714a4Smartijn 	for (p = str, q = tmp; p[0] != '\0' &&
476f95714a4Smartijn 	    ((size_t)(q - tmp) < sizeof(tmp)); p++) {
477f95714a4Smartijn 		if (p[0] == '%') {
478f95714a4Smartijn 			p++;
479f95714a4Smartijn 
480f95714a4Smartijn 			/* Make sure we can find the end of tmp for strlcat */
481f95714a4Smartijn 			q[0] = '\0';
482f95714a4Smartijn 
483f95714a4Smartijn 			/*
484f95714a4Smartijn 			 * Don't need to check strcat for truncation, since we
485f95714a4Smartijn 			 * will bail on the next iteration
486f95714a4Smartijn 			 */
487f95714a4Smartijn 			switch (p[0]) {
488f95714a4Smartijn 			case 'u': /* username */
489f95714a4Smartijn 				q = tmp + strlcat(tmp, ctx->user, sizeof(tmp));
490f95714a4Smartijn 				break;
491f95714a4Smartijn 			case 'h': /* hostname */
492f95714a4Smartijn 				if (gethostname(hostname, sizeof(hostname)) ==
493f95714a4Smartijn 				    -1) {
494f95714a4Smartijn 					dlog(0, "couldn't get host name for "
495f95714a4Smartijn 					    "%%h %s", strerror(errno));
496f95714a4Smartijn 					return NULL;
497f95714a4Smartijn 				}
498f95714a4Smartijn 				q = tmp + strlcat(tmp, hostname, sizeof(tmp));
499f95714a4Smartijn 				break;
500f95714a4Smartijn 			case 'd': /* user dn */
501f95714a4Smartijn 				if (ctx->userdn == NULL) {
502f95714a4Smartijn 					dlog(0, "no userdn has been recorded");
503f95714a4Smartijn 					return 0;
504f95714a4Smartijn 				}
505f95714a4Smartijn 				q = tmp + strlcat(tmp, ctx->userdn,
506f95714a4Smartijn 				    sizeof(tmp));
507f95714a4Smartijn 				break;
508f95714a4Smartijn 			case '%': /* literal % */
509f95714a4Smartijn 				q[0] = p[0];
510f95714a4Smartijn 				q++;
511f95714a4Smartijn 				break;
512f95714a4Smartijn 			default:
513f95714a4Smartijn 				dlog(0, "%s: invalid filter specifier",
514f95714a4Smartijn 				    __func__);
515f95714a4Smartijn 				return NULL;
516f95714a4Smartijn 			}
517f95714a4Smartijn 		} else {
518f95714a4Smartijn 			q[0] = p[0];
519f95714a4Smartijn 			q++;
520f95714a4Smartijn 		}
521f95714a4Smartijn 	}
522f95714a4Smartijn 	if ((size_t) (q - tmp) >= sizeof(tmp)) {
523f95714a4Smartijn 		dlog(0, "filter string too large, unable to process: %s", str);
524f95714a4Smartijn 		return NULL;
525f95714a4Smartijn 	}
526f95714a4Smartijn 
527f95714a4Smartijn 	q[0] = '\0';
528f95714a4Smartijn 	q = strdup(tmp);
529f95714a4Smartijn 	if (q == NULL) {
530f95714a4Smartijn 		dlog(0, "%s", strerror(errno));
531f95714a4Smartijn 		return NULL;
532f95714a4Smartijn 	}
533f95714a4Smartijn 
534f95714a4Smartijn 	return q;
535f95714a4Smartijn }
536