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