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