The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
util.c
Go to the documentation of this file.
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or (at
5 * your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/**
18 * $Id: 698a9a2bd3b0d7d3cce558f03309d4579c5c6362 $
19 * @file lib/ldap/util.c
20 * @brief Utility functions to escape and parse DNs
21 *
22 * @author Arran Cudbard-Bell (a.cudbardb@freeradius.org)
23 * @copyright 2017 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
24 * @copyright 2017 The FreeRADIUS Server Project.
25 */
26RCSID("$Id: 698a9a2bd3b0d7d3cce558f03309d4579c5c6362 $")
27
29
30#include <freeradius-devel/ldap/base.h>
31#include <freeradius-devel/util/base16.h>
32
33#include <freeradius-devel/util/value.h>
34
35#include <stdarg.h>
36
37static const char specials[] = ",+\"\\<>;*=()";
38static const char hextab[] = "0123456789abcdef";
39static const bool escapes[SBUFF_CHAR_CLASS] = {
40 [' '] = true,
41 ['#'] = true,
42 ['='] = true,
43 ['"'] = true,
44 ['+'] = true,
45 [','] = true,
46 [';'] = true,
47 ['<'] = true,
48 ['>'] = true,
49 ['\''] = true
50};
51
52/** Converts "bad" strings into ones which are safe for LDAP
53 *
54 * @note RFC 4515 says filter strings can only use the @verbatim <hex><hex> @endverbatim
55 * format, whereas RFC 4514 indicates that some chars in DNs, may be escaped simply
56 * with a backslash. For simplicity, we always use the hex escape sequences.
57 * In other areas where we're doing DN comparison, the DNs need to be normalised first
58 * so that they both use only hex escape sequences.
59 *
60 * @note This is a callback for xlat operations.
61 *
62 * Will escape any characters in input strings that would cause the string to be interpreted
63 * as part of a DN and or filter. Escape sequence is @verbatim <hex><hex> @endverbatim.
64 *
65 * @param request The current request.
66 * @param out Pointer to output buffer.
67 * @param outlen Size of the output buffer.
68 * @param in Raw unescaped string.
69 * @param arg Any additional arguments (unused).
70 */
71size_t fr_ldap_uri_escape_func(UNUSED request_t *request, char *out, size_t outlen, char const *in, UNUSED void *arg)
72{
73 size_t left = outlen;
74
75 if ((*in == ' ') || (*in == '#')) goto encode;
76
77 while (*in) {
78 /*
79 * Encode unsafe characters.
80 */
81 if (memchr(specials, *in, sizeof(specials) - 1)) {
82 encode:
83 /*
84 * Only 3 or less bytes available.
85 */
86 if (left <= 3) break;
87
88 *out++ = '\\';
89 *out++ = hextab[(*in >> 4) & 0x0f];
90 *out++ = hextab[*in & 0x0f];
91 in++;
92 left -= 3;
93
94 continue;
95 }
96
97 if (left <= 1) break;
98
99 /*
100 * Doesn't need encoding
101 */
102 *out++ = *in++;
103 left--;
104 }
105
106 *out = '\0';
107
108 return outlen - left;
109}
110
112{
113 fr_sbuff_t sbuff;
114 fr_sbuff_uctx_talloc_t sbuff_ctx;
115 size_t len;
116
118
119 if ((vb->type != FR_TYPE_STRING) && (fr_value_box_cast_in_place(vb, vb, FR_TYPE_STRING, NULL) < 0)) {
120 return -1;
121 }
122
123 if (!fr_sbuff_init_talloc(vb, &sbuff, &sbuff_ctx, vb->vb_length * 3, vb->vb_length * 3)) {
124 fr_strerror_printf_push("Failed to allocate buffer for escaped filter");
125 return -1;
126 }
127
128 len = fr_ldap_uri_escape_func(NULL, fr_sbuff_buff(&sbuff), vb->vb_length * 3 + 1, vb->vb_strvalue, NULL);
129
130 /*
131 * If the returned length is unchanged, the value was already safe
132 */
133 if (len == vb->vb_length) {
134 talloc_free(fr_sbuff_buff(&sbuff));
135 } else {
136 fr_sbuff_trim_talloc(&sbuff, len);
138 }
139
140 return 0;
141}
142
143/** Converts escaped DNs and filter strings into normal
144 *
145 * @note RFC 4515 says filter strings can only use the @verbatim <hex><hex> @endverbatim
146 * format, whereas RFC 4514 indicates that some chars in DNs, may be escaped simply
147 * with a backslash..
148 *
149 * Will unescape any special characters in strings, or @verbatim <hex><hex> @endverbatim
150 * sequences.
151 *
152 * @param request The current request.
153 * @param out Pointer to output buffer.
154 * @param outlen Size of the output buffer.
155 * @param in Escaped string string.
156 * @param arg Any additional arguments (unused).
157 */
158size_t fr_ldap_uri_unescape_func(UNUSED request_t *request, char *out, size_t outlen, char const *in, UNUSED void *arg)
159{
160 char const *p;
161 char *c1, *c2, c3;
162 size_t freespace = outlen;
163
164 if (outlen <= 1) return 0;
165
166 p = in;
167 while (*p && (--freespace > 0)) {
168 if (*p != '\\') {
169 next:
170 *out++ = *p++;
171 continue;
172 }
173
174 p++;
175
176 /* It's an escaped special, just remove the slash */
177 if (memchr(specials, *p, sizeof(specials) - 1)) {
178 *out++ = *p++;
179 continue;
180 }
181
182 /* Is a hex sequence */
183 if (!(c1 = memchr(hextab, tolower(p[0]), 16)) ||
184 !(c2 = memchr(hextab, tolower(p[1]), 16))) goto next;
185 c3 = ((c1 - hextab) << 4) + (c2 - hextab);
186
187 *out++ = c3;
188 p += 2;
189 }
190
191 *out = '\0';
192
193 return outlen - freespace;
194}
195
196
197/** Check whether a string looks like a DN
198 *
199 * @param[in] in Str to check.
200 * @param[in] inlen Length of string to check.
201 * @return
202 * - true if string looks like a DN.
203 * - false if string does not look like DN.
204 */
205bool fr_ldap_util_is_dn(char const *in, size_t inlen)
206{
207 char const *p;
208
209 char want = '=';
210 bool too_soon = true;
211 int comp = 1;
212
213 for (p = in; inlen > 0; p++, inlen--) {
214 if (p[0] == '\\') {
215 char c;
216
217 too_soon = false;
218
219 /*
220 * Invalid escape sequence, not a DN
221 */
222 if (inlen < 2) return false;
223
224 /*
225 * Double backslash, consume two chars
226 */
227 if (p[1] == '\\') {
228 inlen--;
229 p++;
230 continue;
231 }
232
233 /*
234 * Special, consume two chars
235 */
236 if (escapes[(uint8_t) p[1]]) {
237 inlen -= 1;
238 p += 1;
239 continue;
240 }
241
242 /*
243 * Invalid escape sequence, not a DN
244 */
245 if (inlen < 3) return false;
246
247 /*
248 * Hex encoding, consume three chars
249 */
250 if (fr_base16_decode(NULL, &FR_DBUFF_TMP((uint8_t *) &c, 1), &FR_SBUFF_IN(p + 1, 2), false) == 1) {
251 inlen -= 2;
252 p += 2;
253 continue;
254 }
255
256 /*
257 * Invalid escape sequence, not a DN
258 */
259 return false;
260 }
261
262 switch (*p) {
263 case '=':
264 if (too_soon || (*p != want)) return false; /* Too soon after last , or = */
265 want = ',';
266 too_soon = true;
267 break;
268
269 case ',':
270 if (too_soon || (*p != want)) return false; /* Too soon after last , or = */
271 want = '=';
272 too_soon = true;
273 comp++;
274 break;
275
276 default:
277 too_soon = false;
278 break;
279 }
280 }
281
282 /*
283 * If the string ended with , or =, or the number
284 * of components was less than 2
285 *
286 * i.e. we don't have <attr>=<val>,<attr>=<val>
287 */
288 if (too_soon || (comp < 2)) return false;
289
290 return true;
291}
292
293/** Parse a subset (just server side sort and virtual list view for now) of LDAP URL extensions
294 *
295 * @param[out] sss Array of LDAPControl * pointers to add controls to.
296 * @param[in] sss_len How many elements remain in the sss array.
297 * @param[in] extensions A NULL terminated array of extensions.
298 * @return
299 * - >0 the number of controls added.
300 * - 0 if no controls added.
301 * - -1 on failure.
302 */
303int fr_ldap_parse_url_extensions(LDAPControl **sss, size_t sss_len, char *extensions[])
304{
305 LDAPControl **sss_p = sss, **sss_end = sss_p + sss_len;
306 int i;
307
308 if (!extensions) {
309 *sss_p = NULL;
310 return 0;
311 }
312
313 /*
314 * Parse extensions in the LDAP URL
315 */
316 for (i = 0; extensions[i]; i++) {
317 fr_sbuff_t sbuff = FR_SBUFF_IN(extensions[i], strlen(extensions[i]));
318 bool is_critical = false;
319
320 if (sss_p == sss_end) {
321 fr_strerror_printf("Too many extensions. Maximum is %ld", sss_len);
322 goto error;
323 }
324
325 if (fr_sbuff_next_if_char(&sbuff, '!')) is_critical = true;
326
327 /*
328 * Server side sort control
329 */
330 if (fr_sbuff_adv_past_str(&sbuff, "sss", 3)) {
331 LDAPSortKey **keys;
332 int ret;
333
334 if (!fr_sbuff_next_if_char(&sbuff, '=')) {
335 LDAPControl **s;
336 fr_strerror_const("Server side sort extension must be "
337 "in the format \"[!]sss=<key>[,key]\"");
338 error:
339 s = sss;
340 while (s < sss_p) {
341 if (*s) ldap_control_free(*s);
342 s++;
343 }
344 return -1;
345 }
346
347 ret = ldap_create_sort_keylist(&keys, fr_sbuff_current(&sbuff));
348 if (ret != LDAP_SUCCESS) {
349 fr_strerror_printf("Invalid server side sort value \"%s\": %s",
350 fr_sbuff_current(&sbuff), ldap_err2string(ret));
351 goto error;
352 }
353
354 if (*sss_p) ldap_control_free(*sss_p);
355
356 ret = ldap_create_sort_control(fr_ldap_handle_thread_local(), keys, is_critical ? 1 : 0, sss_p);
357 ldap_free_sort_keylist(keys);
358 if (ret != LDAP_SUCCESS) {
359 fr_strerror_printf("Failed creating server sort control: %s",
360 ldap_err2string(ret));
361 goto error;
362 }
363 sss_p++;
364 *sss_p = NULL; /* Terminate */
365 continue;
366 }
367
368 if (fr_sbuff_adv_past_str(&sbuff, "vlv", 3)) {
369 LDAPVLVInfo vlvinfo;
370 uint32_t ext_value;
371 struct berval attr_value;
372 int ret;
373
374 if (!fr_sbuff_next_if_char(&sbuff, '=')) {
375 vlv_error:
376 fr_strerror_const("Virtual list view extension must be "
377 "in the format \"[!]vlv=<before>/<after>(/<offset>/<count>|:<value>)");
378 goto error;
379 }
380
381 vlvinfo.ldvlv_context = NULL;
382
383 if (fr_sbuff_out(NULL, &ext_value, &sbuff) <= 0) goto vlv_error;
384 if (!fr_sbuff_next_if_char(&sbuff, '/')) goto vlv_error;
385 vlvinfo.ldvlv_before_count = ext_value;
386
387 if (fr_sbuff_out(NULL, &ext_value, &sbuff) <= 0) goto vlv_error;
388 vlvinfo.ldvlv_after_count = ext_value;
389
390 /* offset/count syntax */
391 if (fr_sbuff_next_if_char(&sbuff, '/')) {
392 /* Ensure attrvalue is null - this is how the type of vlv control is determined */
393 vlvinfo.ldvlv_attrvalue = NULL;
394
395 if (fr_sbuff_out(NULL, &ext_value, &sbuff) <= 0) goto vlv_error;
396 if (!fr_sbuff_next_if_char(&sbuff, '/')) goto error;
397 vlvinfo.ldvlv_offset = ext_value;
398
399 if (fr_sbuff_out(NULL, &ext_value, &sbuff) <= 0) goto vlv_error;
400 vlvinfo.ldvlv_count = ext_value;
401
402 /* greaterThanOrEqual attribute syntax*/
403 } else if (fr_sbuff_next_if_char(&sbuff, ':')) {
404 attr_value.bv_val = fr_sbuff_current(&sbuff);
405 attr_value.bv_len = fr_sbuff_remaining(&sbuff);
406 vlvinfo.ldvlv_attrvalue = &attr_value;
407
408 } else goto error;
409
410 ret = ldap_create_vlv_control(fr_ldap_handle_thread_local(), &vlvinfo, sss_p);
411
412 if (ret != LDAP_SUCCESS) {
413 fr_strerror_printf("Failed creating virtual list view control: %s",
414 ldap_err2string(ret));
415 goto error;
416 }
417
418 sss_p++;
419 *sss_p = NULL; /* Terminate */
420 continue;
421 }
422
423 fr_strerror_printf("URL extension \"%s\" not supported", extensions[i]);
424 return -1;
425 }
426
427 return (sss_end - sss_p);
428}
429
430/** Convert a berval to a talloced string
431 *
432 * The ldap_get_values function is deprecated, and ldap_get_values_len
433 * does not guarantee the berval buffers it returns are \0 terminated.
434 *
435 * For some cases this is fine, for others we require a \0 terminated
436 * buffer (feeding DNs back into libldap for example).
437 *
438 * @param ctx to allocate in.
439 * @param in Berval to copy.
440 * @return \0 terminated buffer containing in->bv_val.
441 */
442char *fr_ldap_berval_to_string(TALLOC_CTX *ctx, struct berval const *in)
443{
444 char *out;
445
446 out = talloc_array(ctx, char, in->bv_len + 1);
447 if (!out) return NULL;
448
449 memcpy(out, in->bv_val, in->bv_len);
450 out[in->bv_len] = '\0';
451
452 return out;
453}
454
455/** Convert a berval to a talloced buffer
456 *
457 * @param ctx to allocate in.
458 * @param in Berval to copy.
459 * @return buffer containing in->bv_val.
460 */
461uint8_t *fr_ldap_berval_to_bin(TALLOC_CTX *ctx, struct berval const *in)
462{
463 uint8_t *out;
464
465 out = talloc_array(ctx, uint8_t, in->bv_len);
466 if (!out) return NULL;
467
468 memcpy(out, in->bv_val, in->bv_len);
469
470 return out;
471}
472
473/** Normalise escape sequences in a DN
474 *
475 * Characters in a DN can either be escaped as
476 * @verbatim <hex><hex> @endverbatim or @verbatim <special> @endverbatim
477 *
478 * The LDAP directory chooses how characters are escaped, which can make
479 * local comparisons of DNs difficult.
480 *
481 * Here we search for hex sequences that match special chars, and convert
482 * them to the @verbatim <special> @endverbatim form.
483 *
484 * @note the resulting output string will only ever be shorter than the
485 * input, so it's fine to use the same buffer for both out and in.
486 *
487 * @param out Where to write the normalised DN.
488 * @param in The input DN.
489 * @return The number of bytes written to out.
490 */
491size_t fr_ldap_util_normalise_dn(char *out, char const *in)
492{
493 char const *p;
494 char *o = out;
495
496 for (p = in; *p != '\0'; p++) {
497 if (p[0] == '\\') {
498 char c = '\0';
499
500 /*
501 * Double backslashes get processed specially
502 */
503 if (p[1] == '\\') {
504 p += 1;
505 *o++ = p[0];
506 *o++ = p[1];
507 continue;
508 }
509
510 /*
511 * Hex encodings that have an alternative
512 * special encoding, get rewritten to the
513 * special encoding.
514 */
515 if (fr_base16_decode(NULL, &FR_DBUFF_TMP((uint8_t *) &c, 1), &FR_SBUFF_IN(p + 1, 2), false) == 1 &&
516 escapes[(uint8_t) c]) {
517 *o++ = '\\';
518 *o++ = c;
519 p += 2;
520 continue;
521 }
522 }
523 *o++ = *p;
524 }
525 *o = '\0';
526
527 return o - out;
528}
529
530/** Find the place at which the two DN strings diverge
531 *
532 * Returns the length of the non matching string in full.
533 *
534 * @param full DN.
535 * @param part Partial DN as returned by ldap_parse_result.
536 * @return
537 * - Length of the portion of full which wasn't matched
538 * - -1 on failure.
539 */
540size_t fr_ldap_common_dn(char const *full, char const *part)
541{
542 size_t f_len, p_len, i;
543
544 if (!full) return -1;
545
546 f_len = strlen(full);
547
548 if (!part) return -1;
549
550 p_len = strlen(part);
551 if (!p_len) return f_len;
552
553 if ((f_len < p_len) || !f_len) return -1;
554
555 for (i = 0; i < p_len; i++) if (part[p_len - i] != full[f_len - i]) return -1;
556
557 return f_len - p_len;
558}
559
560/** Combine filters and tokenize to a tmpl
561 *
562 * @param ctx To allocate combined filter in
563 * @param t_rules Rules for parsing combined filter.
564 * @param sub Array of subfilters (may contain NULLs).
565 * @param sublen Number of potential subfilters in array.
566 * @param out Where to write a pointer to the resulting tmpl.
567 * @return length of combined data.
568 */
569int fr_ldap_filter_to_tmpl(TALLOC_CTX *ctx, tmpl_rules_t const *t_rules, char const **sub, size_t sublen, tmpl_t **out)
570{
571 char *buffer = NULL;
572 char const *in = NULL;
573 ssize_t len = 0;
574 size_t i;
575 int cnt = 0;
576 tmpl_t *parsed;
577
578 *out = NULL;
579
580 /*
581 * Figure out how many filter elements we need to integrate
582 */
583 for (i = 0; i < sublen; i++) {
584 if (sub[i] && *sub[i]) {
585 in = sub[i];
586 cnt++;
587 len += strlen(sub[i]);
588 }
589 }
590
591 if (!cnt) return 0;
592
593 if (cnt > 1) {
594 /*
595 * Allocate a buffer large enough, allowing for (& ... ) plus trailing '\0'
596 */
597 buffer = talloc_array(ctx, char, len + 4);
598
599 strcpy(buffer, "(&");
600 for (i = 0; i < sublen; i++) {
601 if (sub[i] && (*sub[i] != '\0')) {
602 strcat(buffer, sub[i]);
603 }
604 }
605 strcat(buffer, ")");
606 in = buffer;
607 }
608
609 len = tmpl_afrom_substr(ctx, &parsed, &FR_SBUFF_IN_STR(in), T_DOUBLE_QUOTED_STRING, NULL, t_rules);
610
612
613 if (len < 0) {
614 EMARKER(in, -len, fr_strerror());
615 return -1;
616 }
617
618 *out = parsed;
619 return 0;
620}
621
622/** Check that a particular attribute is included in an attribute list
623 *
624 * @param[in] attrs list to check
625 * @param[in] attr to look for
626 * @return
627 * - 1 if attr is in list
628 * - 0 if attr is missing
629 * - -1 if checks not possible
630 */
631int fr_ldap_attrs_check(char const **attrs, char const *attr)
632{
633 size_t len, i;
634
635 if (!attr) return -1;
636
637 len = talloc_array_length(attrs);
638
639 for (i = 0; i < len; i++) {
640 if (!attrs[i]) continue;
641 if (strcasecmp(attrs[i], attr) == 0) return 1;
642 if (strcasecmp(attrs[i], "*") == 0) return 1;
643 }
644
645 return 0;
646}
647
648/** Check an LDAP server entry in URL format is valid
649 *
650 * @param[in,out] handle_config LDAP handle config being built
651 * @param[in] server string to parse
652 * @param[in] cs in which the server is defined
653 * @return
654 * - 0 for valid server definition
655 * - -1 for invalid server definition
656 */
657int fr_ldap_server_url_check(fr_ldap_config_t *handle_config, char const *server, CONF_SECTION const *cs)
658{
659 LDAPURLDesc *ldap_url;
660 bool set_port_maybe = true;
661 int default_port = LDAP_PORT;
662 char *p, *url;
663 CONF_ITEM *ci = (CONF_ITEM *)cf_pair_find(cs, "server");
664
665 if (ldap_url_parse(server, &ldap_url)) {
666 cf_log_err(ci, "Parsing LDAP URL \"%s\" failed", server);
667 ldap_url_error:
668 ldap_free_urldesc(ldap_url);
669 return -1;
670 }
671
672 if (ldap_url->lud_dn && (ldap_url->lud_dn[0] != '\0')) {
673 cf_log_err(ci, "Base DN cannot be specified via server URL");
674 goto ldap_url_error;
675 }
676
677 if (ldap_url->lud_attrs && ldap_url->lud_attrs[0]) {
678 cf_log_err(ci, "Attribute list cannot be speciried via server URL");
679 goto ldap_url_error;
680 }
681
682 /*
683 * ldap_url_parse sets this to base by default.
684 */
685 if (ldap_url->lud_scope != LDAP_SCOPE_BASE) {
686 cf_log_err(ci, "Scope cannot be specified via server URL");
687 goto ldap_url_error;
688 }
689 ldap_url->lud_scope = -1; /* Otherwise LDAP adds ?base */
690
691 /*
692 * The public ldap_url_parse function sets the default
693 * port, so we have to discover whether a port was
694 * included ourselves.
695 */
696 if ((p = strchr(server, ']')) && (p[1] == ':')) { /* IPv6 */
697 set_port_maybe = false;
698 } else if ((p = strchr(server, ':')) && (strchr(p+1, ':') != NULL)) { /* IPv4 */
699 set_port_maybe = false;
700 }
701
702 /*
703 * Figure out the default port from the URL
704 */
705 if (ldap_url->lud_scheme) {
706 if (strcmp(ldap_url->lud_scheme, "ldaps") == 0) {
707 if (handle_config->start_tls == true) {
708 cf_log_err(ci, "ldaps:// scheme is not compatible with 'start_tls'");
709 goto ldap_url_error;
710 }
711 default_port = LDAPS_PORT;
712 handle_config->tls_mode = LDAP_OPT_X_TLS_HARD;
713 } else if (strcmp(ldap_url->lud_scheme, "ldapi") == 0) {
714 set_port_maybe = false;
715 }
716 }
717
718 if (set_port_maybe) {
719 /*
720 * URL port overrides configured port.
721 */
722 ldap_url->lud_port = handle_config->port;
723
724 /*
725 * If there's no URL port, then set it to the default
726 * this is so debugging messages show explicitly
727 * the port we're connecting to.
728 */
729 if (!ldap_url->lud_port) ldap_url->lud_port = default_port;
730 }
731
732 url = ldap_url_desc2str(ldap_url);
733 if (!url) {
734 cf_log_err(ci, "Failed recombining URL components");
735 goto ldap_url_error;
736 }
737 handle_config->server = talloc_asprintf_append(handle_config->server, "%s ", url);
738
739 ldap_free_urldesc(ldap_url);
740 ldap_memfree(url);
741 return (0);
742}
743
744/** Check an LDAP server config in server:port format is valid
745 *
746 * @param[in,out] handle_config LDAP handle config being built
747 * @param[in] server string to parse
748 * @param[in] cs in which the server is defined
749 * @return
750 * - 0 for valid server definition
751 * - -1 for invalid server definition
752 */
753int fr_ldap_server_config_check(fr_ldap_config_t *handle_config, char const *server, CONF_SECTION *cs)
754{
755 char const *p;
756 char *q;
757 int port = 0;
758 size_t len;
759
760 port = handle_config->port;
761
762 /*
763 * We don't support URLs if the library didn't provide
764 * URL parsing functions.
765 */
766 if (strchr(server, '/')) {
767 CONF_ITEM *ci;
768 bad_server_fmt:
769 ci = (CONF_ITEM *)cf_pair_find(cs, "server");
770 cf_log_err(ci, "Invalid 'server' entry, must be in format <server>[:<port>] or "
771 "an ldap URI (ldap|cldap|ldaps|ldapi)://<server>:<port>");
772 return -1;
773 }
774
775 p = strrchr(server, ':');
776 if (p) {
777 port = (int)strtol((p + 1), &q, 10);
778 if ((p == server) || ((p + 1) == q) || (*q != '\0')) goto bad_server_fmt;
779 len = p - server;
780 } else {
781 len = strlen(server);
782 }
783 if (port == 0) port = LDAP_PORT;
784
785 handle_config->server = talloc_asprintf_append(handle_config->server, "ldap://%.*s:%i ",
786 (int)len, server, port);
787 return 0;
788}
789
790/** Translate the error code emitted from ldap_url_parse and friends into something accessible with fr_strerror()
791 *
792 * @param[in] ldap_url_err The error code returned
793 */
794char const *fr_ldap_url_err_to_str(int ldap_url_err)
795{
796 switch (ldap_url_err) {
797 case LDAP_URL_SUCCESS:
798 return "success";
799
800 case LDAP_URL_ERR_MEM:
801 return "no memory";
802
803 case LDAP_URL_ERR_PARAM:
804 return "parameter is bad";
805
806 case LDAP_URL_ERR_BADSCHEME:
807 return "URL doesn't begin with \"[c]ldap[si]://\"";
808
809 case LDAP_URL_ERR_BADENCLOSURE:
810 return "URL is missing trailing \">\"";
811
812 case LDAP_URL_ERR_BADURL:
813 return "URL is bad";
814
815 case LDAP_URL_ERR_BADHOST:
816 return "host/port is bad";
817
818 case LDAP_URL_ERR_BADATTRS:
819 return "bad (or missing) attributes";
820
821 case LDAP_URL_ERR_BADSCOPE:
822 return "scope string is invalid (or missing)";
823
824 case LDAP_URL_ERR_BADFILTER:
825 return "bad or missing filter";
826
827 case LDAP_URL_ERR_BADEXTS:
828 return "bad or missing extensions";
829
830 default:
831 return "unknown reason";
832 }
833}
834
835/** Dump out the contents of an LDAPMessage
836 *
837 * Intended to be called from a debugger.
838 *
839 * @param[in] entry LDAPMessage to dump.
840 */
841void fr_ldap_entry_dump(LDAPMessage *entry)
842{
843 char *dn;
844 BerElement *ber = NULL;
845 char *attr;
846 struct berval **vals;
847 int i;
848 LDAP *ld = fr_ldap_handle_thread_local();
849 int msgtype;
850
851 msgtype = ldap_msgtype(entry);
852 switch (msgtype) {
853 case LDAP_RES_SEARCH_ENTRY:
854 dn = ldap_get_dn(ld, entry);
855 if (dn) {
856 DEBUG("dn: %s", dn);
857 ldap_memfree(dn);
858 }
859
860 for (attr = ldap_first_attribute(ld, entry, &ber);
861 attr != NULL;
862 attr = ldap_next_attribute(ld, entry, ber)) {
863 vals = ldap_get_values_len(ld, entry, attr);
864 if (!vals) {
865 DEBUG("%s: no values", attr);
866 ldap_memfree(attr);
867 continue;
868 }
869
870 for (i = 0; vals[i] != NULL; i++) {
871 bool binary = false;
872 ber_len_t j;
873
874 for (j = 0; j < vals[i]->bv_len; j++) {
875 char c = vals[i]->bv_val[j];
876 if ((c < 32) || (c > 126)) {
877 binary = true;
878 break;
879 }
880 }
881
882 if (binary) {
883 DEBUG("%s[%u]: %pV", attr, i, fr_box_octets((uint8_t *)vals[i]->bv_val, vals[i]->bv_len));
884 continue;
885 }
886
887 DEBUG("%s[%u]: %pV", attr, i, fr_box_strvalue_len(vals[i]->bv_val, vals[i]->bv_len));
888 }
889
890 ldap_value_free_len(vals);
891 ldap_memfree(attr);
892 }
893 break;
894
895 case LDAP_RES_SEARCH_RESULT:
896 case LDAP_RES_BIND:
897 case LDAP_RES_MODIFY:
898 case LDAP_RES_ADD:
899 case LDAP_RES_DELETE:
900 case LDAP_RES_COMPARE:
901 case LDAP_RES_EXTENDED:
902 {
903 int rc;
904 char *matched = NULL;
905 char *errmsg = NULL;
906 char **refs = NULL;
907
908 rc = ldap_parse_result(ld, entry, &msgtype, &matched, &errmsg, &refs, NULL, 0);
909 if (rc != LDAP_SUCCESS) {
910 DEBUG("failed to parse result: %s", ldap_err2string(rc));
911 break;
912 }
913
914 DEBUG("result code: %d (%s)", msgtype, ldap_err2string(msgtype));
915
916 if (matched && *matched) {
917 DEBUG("matched DN: %s", matched);
918 }
919 if (errmsg && *errmsg) {
920 DEBUG("error message: %s", errmsg);
921 }
922 if (refs) {
923 for (i = 0; refs[i] != NULL; i++) {
924 DEBUG("referral: %s", refs[i]);
925 }
926 }
927
928 if (matched) ldap_memfree(matched);
929 if (errmsg) ldap_memfree(errmsg);
930 if (refs) ldap_memvfree((void **)refs);
931 }
932 break;
933
934 default:
935 DEBUG("unhandled LDAP message type: %d", msgtype);
936 break;
937 }
938
939 if (ber) ber_free(ber, 0);
940}
static int const char char buffer[256]
Definition acutest.h:576
strcpy(log_entry->msg, buffer)
#define fr_base16_decode(_err, _out, _in, _no_trailing)
Definition base16.h:92
#define USES_APPLE_DEPRECATED_API
Definition build.h:493
#define RCSID(id)
Definition build.h:506
#define UNUSED
Definition build.h:336
Common header for all CONF_* types.
Definition cf_priv.h:49
A section grouping multiple CONF_PAIR.
Definition cf_priv.h:101
CONF_PAIR * cf_pair_find(CONF_SECTION const *cs, char const *attr)
Search for a CONF_PAIR with a specific name.
Definition cf_util.c:1419
#define cf_log_err(_cf, _fmt,...)
Definition cf_util.h:285
#define FR_DBUFF_TMP(_start, _len_or_end)
Creates a compound literal to pass into functions which accept a dbuff.
Definition dbuff.h:522
#define DEBUG(fmt,...)
Definition dhcpclient.c:38
static fr_slen_t in
Definition dict.h:882
talloc_free(hp)
char * server
Initial server to bind to.
Definition base.h:224
bool start_tls
Send the Start TLS message to the LDAP directory to start encrypted communications using the standard...
Definition base.h:258
uint16_t port
Port to use when binding to the server.
Definition base.h:227
Connection configuration.
Definition base.h:221
LDAP * fr_ldap_handle_thread_local(void)
Get a thread local dummy LDAP handle.
Definition base.c:1129
char const * fr_ldap_url_err_to_str(int ldap_url_err)
Translate the error code emitted from ldap_url_parse and friends into something accessible with fr_st...
Definition util.c:794
static const char hextab[]
Definition util.c:38
size_t fr_ldap_util_normalise_dn(char *out, char const *in)
Normalise escape sequences in a DN.
Definition util.c:491
static const bool escapes[SBUFF_CHAR_CLASS]
Definition util.c:39
size_t fr_ldap_uri_escape_func(UNUSED request_t *request, char *out, size_t outlen, char const *in, UNUSED void *arg)
Converts "bad" strings into ones which are safe for LDAP.
Definition util.c:71
bool fr_ldap_util_is_dn(char const *in, size_t inlen)
Check whether a string looks like a DN.
Definition util.c:205
size_t fr_ldap_common_dn(char const *full, char const *part)
Find the place at which the two DN strings diverge.
Definition util.c:540
int fr_ldap_attrs_check(char const **attrs, char const *attr)
Check that a particular attribute is included in an attribute list.
Definition util.c:631
static USES_APPLE_DEPRECATED_API const char specials[]
Definition util.c:37
uint8_t * fr_ldap_berval_to_bin(TALLOC_CTX *ctx, struct berval const *in)
Convert a berval to a talloced buffer.
Definition util.c:461
int fr_ldap_server_url_check(fr_ldap_config_t *handle_config, char const *server, CONF_SECTION const *cs)
Check an LDAP server entry in URL format is valid.
Definition util.c:657
char * fr_ldap_berval_to_string(TALLOC_CTX *ctx, struct berval const *in)
Convert a berval to a talloced string.
Definition util.c:442
int fr_ldap_box_escape(fr_value_box_t *vb, UNUSED void *uctx)
Definition util.c:111
int fr_ldap_server_config_check(fr_ldap_config_t *handle_config, char const *server, CONF_SECTION *cs)
Check an LDAP server config in server:port format is valid.
Definition util.c:753
size_t fr_ldap_uri_unescape_func(UNUSED request_t *request, char *out, size_t outlen, char const *in, UNUSED void *arg)
Converts escaped DNs and filter strings into normal.
Definition util.c:158
void fr_ldap_entry_dump(LDAPMessage *entry)
Dump out the contents of an LDAPMessage.
Definition util.c:841
int fr_ldap_parse_url_extensions(LDAPControl **sss, size_t sss_len, char *extensions[])
Parse a subset (just server side sort and virtual list view for now) of LDAP URL extensions.
Definition util.c:303
int fr_ldap_filter_to_tmpl(TALLOC_CTX *ctx, tmpl_rules_t const *t_rules, char const **sub, size_t sublen, tmpl_t **out)
Combine filters and tokenize to a tmpl.
Definition util.c:569
#define EMARKER(_str, _marker_idx, _marker)
Definition log.h:232
@ FR_TYPE_STRING
String of printable characters.
unsigned int uint32_t
long int ssize_t
unsigned char uint8_t
int strcasecmp(char *s1, char *s2)
Definition missing.c:65
static int encode(bio_handle_t *h, request_t *request, bio_request_t *u, uint8_t id)
Definition bio.c:1257
static char const * url[FR_RADIUS_FAIL_MAX+1]
#define fr_assert(_expr)
Definition rad_assert.h:37
static int8_t comp(void const *a, void const *b)
Definition rbmonkey.c:13
int fr_sbuff_trim_talloc(fr_sbuff_t *sbuff, size_t len)
Trim a talloced sbuff to the minimum length required to represent the contained string.
Definition sbuff.c:433
size_t fr_sbuff_adv_past_str(fr_sbuff_t *sbuff, char const *needle, size_t needle_len)
Return true and advance past the end of the needle if needle occurs next in the sbuff.
Definition sbuff.c:1753
bool fr_sbuff_next_if_char(fr_sbuff_t *sbuff, char c)
Return true if the current char matches, and if it does, advance.
Definition sbuff.c:2129
#define SBUFF_CHAR_CLASS
Definition sbuff.h:203
#define FR_SBUFF_IN(_start, _len_or_end)
#define fr_sbuff_current(_sbuff_or_marker)
#define fr_sbuff_buff(_sbuff_or_marker)
#define FR_SBUFF_IN_STR(_start)
#define fr_sbuff_out(_err, _out, _in)
#define fr_sbuff_remaining(_sbuff_or_marker)
Talloc sbuff extension structure.
Definition sbuff.h:137
ssize_t tmpl_afrom_substr(TALLOC_CTX *ctx, tmpl_t **out, fr_sbuff_t *in, fr_token_t quote, fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules))
Convert an arbitrary string into a tmpl_t.
Optional arguments passed to vp_tmpl functions.
Definition tmpl.h:336
@ T_DOUBLE_QUOTED_STRING
Definition token.h:119
char const * fr_strerror(void)
Get the last library error.
Definition strerror.c:553
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition strerror.h:64
#define fr_strerror_printf_push(_fmt,...)
Add a message to an existing stack of messages at the tail.
Definition strerror.h:84
#define fr_strerror_const(_msg)
Definition strerror.h:223
int fr_value_box_cast_in_place(TALLOC_CTX *ctx, fr_value_box_t *vb, fr_type_t dst_type, fr_dict_attr_t const *dst_enumv)
Convert one type of fr_value_box_t to another in place.
Definition value.c:4181
void fr_value_box_strdup_shallow_replace(fr_value_box_t *vb, char const *src, ssize_t len)
Free the existing buffer (if talloced) associated with the valuebox, and replace it with a new one.
Definition value.c:4730
#define fr_box_strvalue_len(_val, _len)
Definition value.h:309
#define fr_value_box_is_safe_for(_box, _safe_for)
Definition value.h:1100
static size_t char fr_sbuff_t size_t inlen
Definition value.h:1030
static size_t char ** out
Definition value.h:1030
#define fr_box_octets(_val, _len)
Definition value.h:311