]> git.ipfire.org Git - thirdparty/squid.git/blob - src/acl/external/kerberos_ldap_group/support_resolv.cc
Source Format Enforcement (#763)
[thirdparty/squid.git] / src / acl / external / kerberos_ldap_group / support_resolv.cc
1 /*
2 * Copyright (C) 1996-2021 The Squid Software Foundation and contributors
3 *
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
7 */
8
9 /*
10 * -----------------------------------------------------------------------------
11 *
12 * Author: Markus Moeller (markus_moeller at compuserve.com)
13 *
14 * Copyright (C) 2007 Markus Moeller. All rights reserved.
15 *
16 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 2 of the License, or
19 * (at your option) any later version.
20 *
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
25 *
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, write to the Free Software
28 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
29 *
30 * -----------------------------------------------------------------------------
31 */
32
33 #include "squid.h"
34 #include "util.h"
35
36 #if HAVE_LDAP
37
38 #include "support.h"
39 #include <cerrno>
40 #if HAVE_NETDB_H
41 #include <netdb.h>
42 #endif
43 #if HAVE_NETINET_IN_H
44 #include <netinet/in.h>
45 #endif
46 #if HAVE_RESOLV_H
47 #include <resolv.h>
48 #endif
49 #if HAVE_ARPA_NAMESER_H
50 #include <arpa/nameser.h>
51 #endif
52
53 void nsError(int nserror, char *server);
54 static int compare_hosts(struct hstruct *h1, struct hstruct *h2);
55 static void swap(struct hstruct *a, struct hstruct *b);
56 static void sort(struct hstruct *array, int nitems, int (*cmp) (struct hstruct *, struct hstruct *), int begin, int end);
57 static void msort(struct hstruct *array, size_t nitems, int (*cmp) (struct hstruct *, struct hstruct *));
58
59 /*
60 * See http://www.ietf.org/rfc/rfc1035.txt
61 */
62 /*
63 * See http://www.ietf.org/rfc/rfc2782.txt
64 *
65 */
66 void
67 nsError(int nserror, char *service)
68 {
69 switch (nserror) {
70 case HOST_NOT_FOUND:
71 error((char *) "%s| %s: ERROR: res_search: Unknown service record: %s\n", LogTime(), PROGRAM, service);
72 break;
73 case NO_DATA:
74 error((char *) "%s| %s: ERROR: res_search: No SRV record for %s\n", LogTime(), PROGRAM, service);
75 break;
76 case TRY_AGAIN:
77 error((char *) "%s| %s: ERROR: res_search: No response for SRV query\n", LogTime(), PROGRAM);
78 break;
79 default:
80 error((char *) "%s| %s: ERROR: res_search: Unexpected error: %s\n", LogTime(), PROGRAM, strerror(nserror));
81 }
82 }
83
84 static void
85 swap(struct hstruct *a, struct hstruct *b)
86 {
87 struct hstruct c;
88
89 c.host = a->host;
90 c.priority = a->priority;
91 c.weight = a->weight;
92 a->host = b->host;
93 a->priority = b->priority;
94 a->weight = b->weight;
95 b->host = c.host;
96 b->priority = c.priority;
97 b->weight = c.weight;
98 }
99
100 static void
101 sort(struct hstruct *array, int nitems, int (*cmp) (struct hstruct *, struct hstruct *), int begin, int end)
102 {
103 if (end > begin) {
104 int l = begin + 1;
105 int r = end;
106 while (l < r) {
107 int pivot = begin;
108 if (cmp(&array[l], &array[pivot]) <= 0) {
109 l += 1;
110 } else {
111 r -= 1;
112 swap(&array[l], &array[r]);
113 }
114 }
115 l -= 1;
116 swap(&array[begin], &array[l]);
117 sort(array, nitems, cmp, begin, l);
118 sort(array, nitems, cmp, r, end);
119 }
120 }
121
122 static void
123 msort(struct hstruct *array, size_t nitems, int (*cmp) (struct hstruct *, struct hstruct *))
124 {
125 sort(array, (int)nitems, cmp, 0, (int)(nitems - 1));
126 }
127
128 static int
129 compare_hosts(struct hstruct *host1, struct hstruct *host2)
130 {
131 /*
132 *
133 * The comparison function must return an integer less than, equal to,
134 * or greater than zero if the first argument is considered to be
135 * respectively less than, equal to, or greater than the second.
136 */
137 if ((host1->priority < host2->priority) && (host1->priority != -1))
138 return -1;
139 if ((host1->priority < host2->priority) && (host1->priority == -1))
140 return 1;
141 if ((host1->priority > host2->priority) && (host2->priority != -1))
142 return 1;
143 if ((host1->priority > host2->priority) && (host2->priority == -1))
144 return -1;
145 if (host1->priority == host2->priority) {
146 if (host1->weight > host2->weight)
147 return -1;
148 if (host1->weight < host2->weight)
149 return 1;
150 }
151 return 0;
152 }
153
154 size_t
155 free_hostname_list(struct hstruct **hlist, size_t nhosts)
156 {
157 struct hstruct *hp = NULL;
158 size_t i;
159
160 hp = *hlist;
161 for (i = 0; i < nhosts; ++i) {
162 xfree(hp[i].host);
163 }
164
165 safe_free(hp);
166 *hlist = hp;
167 return 0;
168 }
169
170 size_t
171 get_hostname_list(struct hstruct **hlist, size_t nhosts, char *name)
172 {
173 struct addrinfo *hres = NULL, *hres_list;
174 int rc, count;
175 struct hstruct *hp = NULL;
176
177 if (!name)
178 return (nhosts);
179
180 hp = *hlist;
181 rc = getaddrinfo((const char *) name, NULL, NULL, &hres);
182 if (rc != 0) {
183 error((char *) "%s| %s: ERROR: Error while resolving hostname with getaddrinfo: %s\n", LogTime(), PROGRAM, gai_strerror(rc));
184 return (nhosts);
185 }
186 hres_list = hres;
187 count = 0;
188 while (hres_list) {
189 ++count;
190 hres_list = hres_list->ai_next;
191 }
192 hres_list = hres;
193 count = 0;
194 while (hres_list) {
195 /*
196 * char host[sysconf(_SC_HOST_NAME_MAX)];
197 */
198 char host[1024];
199 rc = getnameinfo(hres_list->ai_addr, hres_list->ai_addrlen, host, sizeof(host), NULL, 0, 0);
200 if (rc != 0) {
201 error((char *) "%s| %s: ERROR: Error while resolving ip address with getnameinfo: %s\n", LogTime(), PROGRAM, gai_strerror(rc));
202 freeaddrinfo(hres);
203 *hlist = hp;
204 return (nhosts);
205 }
206 ++count;
207 debug((char *) "%s| %s: DEBUG: Resolved address %d of %s to %s\n", LogTime(), PROGRAM, count, name, host);
208
209 hp = (struct hstruct *) xrealloc(hp, sizeof(struct hstruct) * (nhosts + 1));
210 hp[nhosts].host = xstrdup(host);
211 hp[nhosts].port = -1;
212 hp[nhosts].priority = -1;
213 hp[nhosts].weight = -1;
214 ++nhosts;
215
216 hres_list = hres_list->ai_next;
217 }
218
219 freeaddrinfo(hres);
220 *hlist = hp;
221 return (nhosts);
222 }
223
224 size_t
225 get_ldap_hostname_list(struct main_args *margs, struct hstruct **hlist, size_t nh, char *domain)
226 {
227
228 /*
229 * char name[sysconf(_SC_HOST_NAME_MAX)];
230 */
231 char name[1024];
232 char *service = NULL;
233 struct hstruct *hp = NULL;
234 struct lsstruct *ls = NULL;
235 size_t nhosts = 0;
236 int size;
237 int len, olen;
238 size_t i, j, k;
239 u_char *buffer = NULL;
240 u_char *p;
241
242 ls = margs->lservs;
243 while (ls) {
244 debug((char *) "%s| %s: DEBUG: Ldap server loop: lserver@domain %s@%s\n", LogTime(), PROGRAM, ls->lserver, ls->domain?ls->domain:"NULL");
245 if (ls->domain && !strcasecmp(ls->domain, domain)) {
246 debug((char *) "%s| %s: DEBUG: Found lserver@domain %s@%s\n", LogTime(), PROGRAM, ls->lserver, ls->domain);
247 hp = (struct hstruct *) xrealloc(hp, sizeof(struct hstruct) * (nhosts + 1));
248 hp[nhosts].host = xstrdup(ls->lserver);
249 hp[nhosts].port = -1;
250 hp[nhosts].priority = -2;
251 hp[nhosts].weight = -2;
252 ++nhosts;
253 } else if ( !ls->domain || !strcasecmp(ls->domain, "") ) {
254 debug((char *) "%s| %s: DEBUG: Found lserver@domain %s@%s\n", LogTime(), PROGRAM, ls->lserver, ls->domain?ls->domain:"NULL");
255 hp = (struct hstruct *) xrealloc(hp, sizeof(struct hstruct) * (nhosts + 1));
256 hp[nhosts].host = xstrdup(ls->lserver);
257 hp[nhosts].port = -1;
258 hp[nhosts].priority = -2;
259 hp[nhosts].weight = -2;
260 ++nhosts;
261
262 }
263 ls = ls->next;
264 }
265 /* found ldap servers in predefined list -> exit */
266 if (nhosts > 0)
267 goto cleanup;
268
269 if (margs->ssl) {
270 service = (char *) xmalloc(strlen("_ldaps._tcp.") + strlen(domain) + 1);
271 strcpy(service, "_ldaps._tcp.");
272 } else {
273 service = (char *) xmalloc(strlen("_ldap._tcp.") + strlen(domain) + 1);
274 strcpy(service, "_ldap._tcp.");
275 }
276 strcat(service, domain);
277
278 #ifndef PACKETSZ_MULT
279 /*
280 * It seems Solaris doesn't give back the real length back when res_search uses a to small buffer
281 * Set a bigger one here
282 */
283 #define PACKETSZ_MULT 10
284 #endif
285
286 hp = *hlist;
287 buffer = (u_char *) xmalloc(PACKETSZ_MULT * NS_PACKETSZ);
288 if ((len = res_search(service, ns_c_in, ns_t_srv, (u_char *) buffer, PACKETSZ_MULT * NS_PACKETSZ)) < 0) {
289 error((char *) "%s| %s: ERROR: Error while resolving service record %s with res_search\n", LogTime(), PROGRAM, service);
290 nsError(h_errno, service);
291 if (margs->ssl) {
292 xfree(service);
293 service = (char *) xmalloc(strlen("_ldap._tcp.") + strlen(domain) + 1);
294 strcpy(service, "_ldap._tcp.");
295 strcat(service, domain);
296 if ((len = res_search(service, ns_c_in, ns_t_srv, (u_char *) buffer, PACKETSZ_MULT * NS_PACKETSZ)) < 0) {
297 error((char *) "%s| %s: ERROR: Error while resolving service record %s with res_search\n", LogTime(), PROGRAM, service);
298 nsError(h_errno, service);
299 goto finalise;
300 }
301 } else {
302 goto finalise;
303 }
304 }
305 if (len > PACKETSZ_MULT * NS_PACKETSZ) {
306 olen = len;
307 buffer = (u_char *) xrealloc(buffer, (size_t)len);
308 if ((len = res_search(service, ns_c_in, ns_t_srv, (u_char *) buffer, len)) < 0) {
309 error((char *) "%s| %s: ERROR: Error while resolving service record %s with res_search\n", LogTime(), PROGRAM, service);
310 nsError(h_errno, service);
311 goto finalise;
312 }
313 if (len > olen) {
314 error((char *) "%s| %s: ERROR: Reply to big: buffer: %d reply length: %d\n", LogTime(), PROGRAM, olen, len);
315 goto finalise;
316 }
317 }
318 p = buffer;
319 p += 6 * NS_INT16SZ; /* Header(6*16bit) = id + flags + 4*section count */
320 if (p > buffer + len) {
321 error((char *) "%s| %s: ERROR: Message to small: %d < header size\n", LogTime(), PROGRAM, len);
322 goto finalise;
323 }
324 if ((size = dn_expand(buffer, buffer + len, p, name, sizeof(name))) < 0) {
325 error((char *) "%s| %s: ERROR: Error while expanding query name with dn_expand: %s\n", LogTime(), PROGRAM, strerror(errno));
326 goto finalise;
327 }
328 p += size; /* Query name */
329 p += 2 * NS_INT16SZ; /* Query type + class (2*16bit) */
330 if (p > buffer + len) {
331 error((char *) "%s| %s: ERROR: Message to small: %d < header + query name,type,class \n", LogTime(), PROGRAM, len);
332 goto finalise;
333 }
334 while (p < buffer + len) {
335 int type, rdlength;
336 if ((size = dn_expand(buffer, buffer + len, p, name, sizeof(name))) < 0) {
337 error((char *) "%s| %s: ERROR: Error while expanding answer name with dn_expand: %s\n", LogTime(), PROGRAM, strerror(errno));
338 goto finalise;
339 }
340 p += size; /* Resource Record name */
341 if (p > buffer + len) {
342 error((char *) "%s| %s: ERROR: Message to small: %d < header + query name,type,class + answer name\n", LogTime(), PROGRAM, len);
343 goto finalise;
344 }
345 NS_GET16(type, p); /* RR type (16bit) */
346 p += NS_INT16SZ + NS_INT32SZ; /* RR class + ttl (16bit+32bit) */
347 if (p > buffer + len) {
348 error((char *) "%s| %s: ERROR: Message to small: %d < header + query name,type,class + answer name + RR type,class,ttl\n", LogTime(), PROGRAM, len);
349 goto finalise;
350 }
351 NS_GET16(rdlength, p); /* RR data length (16bit) */
352
353 if (type == ns_t_srv) { /* SRV record */
354 int priority, weight, port;
355 char host[NS_MAXDNAME];
356 if (p > buffer + len) {
357 error((char *) "%s| %s: ERROR: Message to small: %d < header + query name,type,class + answer name + RR type,class,ttl + RR data length\n", LogTime(), PROGRAM, len);
358 goto finalise;
359 }
360 NS_GET16(priority, p); /* Priority (16bit) */
361 if (p > buffer + len) {
362 error((char *) "%s| %s: ERROR: Message to small: %d < SRV RR + priority\n", LogTime(), PROGRAM, len);
363 goto finalise;
364 }
365 NS_GET16(weight, p); /* Weight (16bit) */
366 if (p > buffer + len) {
367 error((char *) "%s| %s: ERROR: Message to small: %d < SRV RR + priority + weight\n", LogTime(), PROGRAM, len);
368 goto finalise;
369 }
370 NS_GET16(port, p); /* Port (16bit) */
371 if (p > buffer + len) {
372 error((char *) "%s| %s: ERROR: Message to small: %d < SRV RR + priority + weight + port\n", LogTime(), PROGRAM, len);
373 goto finalise;
374 }
375 if ((size = dn_expand(buffer, buffer + len, p, host, NS_MAXDNAME)) < 0) {
376 error((char *) "%s| %s: ERROR: Error while expanding SRV RR name with dn_expand: %s\n", LogTime(), PROGRAM, strerror(errno));
377 goto finalise;
378 }
379 debug((char *) "%s| %s: DEBUG: Resolved SRV %s record to %s\n", LogTime(), PROGRAM, service, host);
380 hp = (struct hstruct *) xrealloc(hp, sizeof(struct hstruct) * (nh + 1));
381 hp[nh].host = xstrdup(host);
382 hp[nh].port = port;
383 hp[nh].priority = priority;
384 hp[nh].weight = weight;
385 ++nh;
386 p += size;
387 } else {
388 p += rdlength;
389 }
390 if (p > buffer + len) {
391 error((char *) "%s| %s: ERROR: Message to small: %d < SRV RR + priority + weight + port + name\n", LogTime(), PROGRAM, len);
392 goto finalise;
393 }
394 }
395 if (p != buffer + len) {
396 #if (SIZEOF_LONG == 8)
397 error("%s| %s: ERROR: Inconsistence message length: %ld!=0\n", LogTime(), PROGRAM, buffer + len - p);
398 #else
399 error((char *) "%s| %s: ERROR: Inconsistence message length: %d!=0\n", LogTime(), PROGRAM, buffer + len - p);
400 #endif
401 goto finalise;
402 }
403
404 finalise:
405 nhosts = get_hostname_list(&hp, nh, domain);
406
407 debug("%s| %s: DEBUG: Adding %s to list\n", LogTime(), PROGRAM, domain);
408
409 hp = (struct hstruct *) xrealloc(hp, sizeof(struct hstruct) * (nhosts + 1));
410 hp[nhosts].host = xstrdup(domain);
411 hp[nhosts].port = -1;
412 hp[nhosts].priority = -2;
413 hp[nhosts].weight = -2;
414 ++nhosts;
415
416 cleanup:
417 /* Remove duplicates */
418 for (i = 0; i < nhosts; ++i) {
419 for (j = i + 1; j < nhosts; ++j) {
420 if (!strcasecmp(hp[i].host, hp[j].host)) {
421 if (hp[i].port == hp[j].port ||
422 (hp[i].port == -1 && hp[j].port == 389) ||
423 (hp[i].port == 389 && hp[j].port == -1)) {
424 xfree(hp[j].host);
425 for (k = j + 1; k < nhosts; ++k) {
426 hp[k - 1].host = hp[k].host;
427 hp[k - 1].port = hp[k].port;
428 hp[k - 1].priority = hp[k].priority;
429 hp[k - 1].weight = hp[k].weight;
430 }
431 --j;
432 --nhosts;
433 hp = (struct hstruct *) xrealloc(hp, sizeof(struct hstruct) * (nhosts + 1));
434 }
435 }
436 }
437 }
438
439 /* Sort by Priority / Weight */
440 msort(hp, (size_t)nhosts, compare_hosts);
441
442 if (debug_enabled) {
443 debug((char *) "%s| %s: DEBUG: Sorted ldap server names for domain %s:\n", LogTime(), PROGRAM, domain);
444 for (i = 0; i < nhosts; ++i) {
445 debug((char *) "%s| %s: DEBUG: Host: %s Port: %d Priority: %d Weight: %d\n", LogTime(), PROGRAM, hp[i].host, hp[i].port, hp[i].priority, hp[i].weight);
446 }
447 }
448 xfree(buffer);
449 xfree(service);
450 *hlist = hp;
451 return (nhosts);
452 }
453 #endif
454