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