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