The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
directory.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: 9fe1c5b3c4c359e9bccf37262d43efd6380fa270 $
19 * @file lib/ldap/directory.c
20 * @brief Determine remote server implementation and capabilities.
21 *
22 * As described by http://ldapwiki.willeke.com/wiki/Determine%20LDAP%20Server%20Vendor
23 *
24 * @copyright 2016 The FreeRADIUS Server Project.
25 * @copyright 2016 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
26 */
27RCSID("$Id: 9fe1c5b3c4c359e9bccf37262d43efd6380fa270 $")
28
30
31#define LOG_PREFIX name
32
33#include <freeradius-devel/ldap/base.h>
34
36 { L("Active Directory"), FR_LDAP_DIRECTORY_ACTIVE_DIRECTORY },
37 { L("IBM"), FR_LDAP_DIRECTORY_IBM },
38 { L("NetScape"), FR_LDAP_DIRECTORY_NETSCAPE },
39 { L("OpenLDAP"), FR_LDAP_DIRECTORY_OPENLDAP },
40 { L("Oracle Internet Directory"), FR_LDAP_DIRECTORY_ORACLE_INTERNET_DIRECTORY },
41 { L("Oracle Unified Directory"), FR_LDAP_DIRECTORY_ORACLE_UNIFIED_DIRECTORY },
42 { L("Oracle Virtual Directory"), FR_LDAP_DIRECTORY_ORACLE_VIRTUAL_DIRECTORY },
43 { L("Samba"), FR_LDAP_DIRECTORY_SAMBA },
44 { L("Siemens AG"), FR_LDAP_DIRECTORY_SIEMENS_AG },
45 { L("Sun One Directory"), FR_LDAP_DIRECTORY_SUN_ONE_DIRECTORY },
46 { L("Unbound ID"), FR_LDAP_DIRECTORY_UNBOUND_ID },
47 { L("Unknown"), FR_LDAP_DIRECTORY_UNKNOWN },
48 { L("eDirectory"), FR_LDAP_DIRECTORY_EDIRECTORY }
49};
51
53 LDAPMessage *result, char const *name)
54{
55 int entry_cnt, i, num, ldap_errno;
56 LDAPMessage *entry;
57 struct berval **values = NULL;
58
59 entry_cnt = ldap_count_entries(handle, result);
60 if (entry_cnt != 1) {
61 WARN("Capability check failed: Ambiguous result for rootDSE, expected 1 entry, got %i", entry_cnt);
62 return 1;
63 }
64
65 entry = ldap_first_entry(handle, result);
66 if (!entry) {
67 ldap_get_option(handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
68
69 WARN("Capability check failed: Failed retrieving entry: %s", ldap_err2string(ldap_errno));
70 return 1;
71 }
72
73 values = ldap_get_values_len(handle, entry, "vendorname");
74 if (values) {
75 directory->vendor_str = fr_ldap_berval_to_string(directory, values[0]);
76 INFO("Directory vendor: %s", directory->vendor_str);
77
78 ldap_value_free_len(values);
79 }
80
81 values = ldap_get_values_len(handle, entry, "vendorversion");
82 if (values) {
83 directory->version_str = fr_ldap_berval_to_string(directory, values[0]);
84 INFO("Directory version: %s", directory->version_str);
85
86 ldap_value_free_len(values);
87 }
88
89 if (directory->vendor_str) {
90 if (strcasestr(directory->vendor_str, "International Business Machines")) {
91 directory->type = FR_LDAP_DIRECTORY_IBM;
92 } else if (strcasestr(directory->vendor_str, "Samba Team")) {
93 directory->type = FR_LDAP_DIRECTORY_SAMBA;
94 }
95
96 goto found;
97 }
98
99 if (directory->version_str) {
100 /*
101 * Novell eDirectory vendorversion contains eDirectory
102 */
103 if (strcasestr(directory->version_str, "eDirectory")) {
105 /*
106 * Oracle unified directory vendorversion contains Oracle Unified Directory
107 */
108 } else if (strcasestr(directory->version_str, "Oracle Unified Directory")) {
110 /*
111 * Unbound directory vendorversion contains UnboundID
112 */
113 } else if (strcasestr(directory->version_str, "UnboundID")) {
115 /*
116 * NetScape directory venderversion contains Netscape-Directory
117 */
118 } else if (strcasestr(directory->version_str, "Netscape-Directory")) {
119 directory->type = FR_LDAP_DIRECTORY_NETSCAPE;
120 /*
121 * Siemens AG directory vendorversion contains DirX Directory
122 */
123 } else if (strcasestr(directory->version_str, "DirX Directory")) {
125 /*
126 * Sun One Directory vendorversion contains Sun Java
127 */
128 } else if (strcasestr(directory->version_str, "Sun Java")) {
130 }
131 goto found;
132 }
133
134 /*
135 * isGlobalCatalogReady is only present on ActiveDirectory
136 * instances. AD doesn't provide vendorname or vendorversion
137 */
138 values = ldap_get_values_len(handle, entry, "isGlobalCatalogReady");
139 if (values) {
141 ldap_value_free_len(values);
142 goto found;
143 }
144
145 /*
146 * OpenLDAP has a special objectClass for its RootDSE
147 */
148 values = ldap_get_values_len(handle, entry, "objectClass");
149 if (values) {
150 num = ldap_count_values_len(values);
151 for (i = 0; i < num; i++) {
152 if (strncmp("OpenLDAProotDSE", values[i]->bv_val, values[i]->bv_len) == 0) {
153 directory->type = FR_LDAP_DIRECTORY_OPENLDAP;
154 }
155 }
156 ldap_value_free_len(values);
157 goto found;
158 }
159
160 /*
161 * Oracle Virtual Directory and Oracle Internet Directory
162 */
163 values = ldap_get_values_len(handle, entry, "orcldirectoryversion");
164 if (values) {
165 if (memmem(values[0]->bv_val, values[0]->bv_len, "OID", 3)) {
167 } else if (memmem(values[0]->bv_val, values[0]->bv_len, "OVD", 3)) {
169 }
170 ldap_value_free_len(values);
171 }
172
173found:
174 INFO("Directory type: %s", fr_table_str_by_value(fr_ldap_directory_type_table, directory->type, "<INVALID>"));
175
176 switch (directory->type) {
180 directory->cleartext_password = false;
181 break;
182
183 default:
184 directory->cleartext_password = true;
185 break;
186 }
187
188 /*
189 * Evaluate what type of sync the directory supports
190 */
191 values = ldap_get_values_len(handle, entry, "supportedControl");
192 if (values) {
193 num = ldap_count_values_len(values);
194 for (i = 0; i < num; i++) {
195 if (strncmp(LDAP_CONTROL_SYNC, values[i]->bv_val, values[i]->bv_len) == 0) {
196 INFO("Directory supports RFC 4533");
197 directory->sync_type = FR_LDAP_SYNC_RFC4533;
198 break;
199 }
200 if (strncmp(LDAP_SERVER_NOTIFICATION_OID, values[i]->bv_val, values[i]->bv_len) == 0) {
201 INFO("Directory supports LDAP_SERVER_NOTIFICATION_OID");
203 break;
204 }
205 if (strncmp(LDAP_CONTROL_PERSIST_REQUEST, values[i]->bv_val, values[i]->bv_len) == 0) {
206 INFO("Directory supports persistent search");
208 break;
209 }
210 }
211 ldap_value_free_len(values);
212 } else {
213 WARN("No supportedControl returned by LDAP server");
214 }
215
216 /*
217 * Extract naming contexts
218 */
219 values = ldap_get_values_len(handle, entry, "namingContexts");
220 if (!values) return 0;
221
222 num = ldap_count_values_len(values);
223 directory->naming_contexts = talloc_array(directory, char const *, num);
224 for (i = 0; i < num; i++) {
225 directory->naming_contexts[i] = fr_ldap_berval_to_string(directory, values[i]);
226 }
227 ldap_value_free_len(values);
228
229 return 0;
230}
231
232/** Parse results of search on rootDSE to gather data on LDAP server
233 *
234 * @param[in] handle on which the query was run.
235 * @param[in] query which requested the rootDSE.
236 * @param[in] result head of LDAP results message chain.
237 * @param[in] rctx LDAP directory whose properties are to be populated.
238 */
239static void ldap_trunk_directory_alloc_read(LDAP *handle, fr_ldap_query_t *query, LDAPMessage *result, void *rctx)
240{
241 fr_ldap_config_t const *config = query->ldap_conn->config;
242 fr_ldap_directory_t *directory = talloc_get_type_abort(rctx, fr_ldap_directory_t);
243
244 (void)fr_ldap_directory_result_parse(directory, handle, result, config->name);
245}
246
247/** Async extract useful information from the rootDSE of the LDAP server
248 *
249 * This is called once for each new thread trunk when it first connects.
250 *
251 * @param[in] ctx to allocate fr_ldap_directory_t in.
252 * @param[in] ttrunk Thread trunk connection to be queried
253 * @return
254 * - 0 on success
255 * < 0 on failure
256 */
258{
259 fr_ldap_query_t *query;
260 static char const *attrs[] = LDAP_DIRECTORY_ATTRS;
261 trunk_request_t *treq;
262
263 ttrunk->directory = talloc_zero(ctx, fr_ldap_directory_t);
264 if (!ttrunk->directory) return -1;
265
266 treq = trunk_request_alloc(ttrunk->trunk, NULL);
267 if (!treq) return -1;
268
269 query = fr_ldap_search_alloc(treq, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, NULL, NULL);
271 query->treq = treq;
272
273 trunk_request_enqueue(&query->treq, ttrunk->trunk, NULL, query, ttrunk->directory);
274
275 return 0;
276}
277
278/** Async extract useful information from the rootDSE of the LDAP server
279 *
280 * This version is for a single connection rather than a connection trunk
281 *
282 * @param[in] ldap_conn connection to be queried
283 * @return
284 * - message ID on success
285 * < 0 on failure
286 */
288{
289 int msgid;
290 static char const *attrs[] = LDAP_DIRECTORY_ATTRS;
291
292 ldap_conn->directory = talloc_zero(ldap_conn, fr_ldap_directory_t);
293 if (!ldap_conn->directory) return -1;
294
295 if (fr_ldap_search_async(&msgid, NULL, ldap_conn, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs,
296 NULL, NULL) != LDAP_PROC_SUCCESS) return -1;
297
298 return msgid;
299}
#define USES_APPLE_DEPRECATED_API
Definition build.h:470
#define RCSID(id)
Definition build.h:483
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:209
#define NUM_ELEMENTS(_t)
Definition build.h:337
int fr_ldap_conn_directory_alloc_async(fr_ldap_connection_t *ldap_conn)
Async extract useful information from the rootDSE of the LDAP server.
Definition directory.c:287
int fr_ldap_trunk_directory_alloc_async(TALLOC_CTX *ctx, fr_ldap_thread_trunk_t *ttrunk)
Async extract useful information from the rootDSE of the LDAP server.
Definition directory.c:257
static fr_table_num_sorted_t const fr_ldap_directory_type_table[]
Definition directory.c:35
static void ldap_trunk_directory_alloc_read(LDAP *handle, fr_ldap_query_t *query, LDAPMessage *result, void *rctx)
Parse results of search on rootDSE to gather data on LDAP server.
Definition directory.c:239
int fr_ldap_directory_result_parse(fr_ldap_directory_t *directory, LDAP *handle, LDAPMessage *result, char const *name)
Definition directory.c:52
static size_t fr_ldap_directory_type_table_len
Definition directory.c:50
#define LDAP_DIRECTORY_ATTRS
Definition base.h:824
#define LDAP_SERVER_NOTIFICATION_OID
OID of Active Directory control for persistent search.
Definition base.h:115
@ FR_LDAP_DIRECTORY_ORACLE_UNIFIED_DIRECTORY
Directory server is Oracle Unified Directory.
Definition base.h:149
@ FR_LDAP_DIRECTORY_UNKNOWN
We can't determine the directory server.
Definition base.h:141
@ FR_LDAP_DIRECTORY_NETSCAPE
Directory server is Netscape.
Definition base.h:146
@ FR_LDAP_DIRECTORY_EDIRECTORY
Directory server is eDir.
Definition base.h:144
@ FR_LDAP_DIRECTORY_ORACLE_INTERNET_DIRECTORY
Directory server is Oracle Internet Directory.
Definition base.h:148
@ FR_LDAP_DIRECTORY_UNBOUND_ID
Directory server is Unbound ID.
Definition base.h:153
@ FR_LDAP_DIRECTORY_SIEMENS_AG
Directory server is Siemens AG.
Definition base.h:152
@ FR_LDAP_DIRECTORY_ORACLE_VIRTUAL_DIRECTORY
Directory server is Oracle Virtual Directory.
Definition base.h:150
@ FR_LDAP_DIRECTORY_ACTIVE_DIRECTORY
Directory server is Active Directory.
Definition base.h:143
@ FR_LDAP_DIRECTORY_OPENLDAP
Directory server is OpenLDAP.
Definition base.h:147
@ FR_LDAP_DIRECTORY_SUN_ONE_DIRECTORY
Directory server is Sun One Directory.
Definition base.h:151
@ FR_LDAP_DIRECTORY_IBM
Directory server is IBM.
Definition base.h:145
@ FR_LDAP_DIRECTORY_SAMBA
Directory server is Samba.
Definition base.h:154
fr_ldap_sync_type_t sync_type
What kind of LDAP sync this directory supports.
Definition base.h:211
fr_ldap_result_parser_t parser
Custom results parser.
Definition base.h:466
fr_ldap_directory_t * directory
The type of directory we're connected to.
Definition base.h:342
trunk_request_t * treq
Trunk request this query is associated with.
Definition base.h:456
char * fr_ldap_berval_to_string(TALLOC_CTX *ctx, struct berval const *in)
Convert a berval to a talloced string.
Definition util.c:390
fr_ldap_config_t const * config
rlm_ldap connection configuration.
Definition base.h:344
char const * vendor_str
As returned from the vendorName attribute in the rootDSE.
Definition base.h:202
fr_ldap_connection_t * ldap_conn
LDAP connection this query is running on.
Definition base.h:457
bool cleartext_password
Whether the server will return the user's plaintext password.
Definition base.h:208
@ FR_LDAP_SYNC_ACTIVE_DIRECTORY
Directory supports AD style persistent search.
Definition base.h:160
@ FR_LDAP_SYNC_PERSISTENT_SEARCH
Directory supports persistent search.
Definition base.h:161
@ FR_LDAP_SYNC_RFC4533
Directory supports RFC 4533.
Definition base.h:159
fr_ldap_directory_t * directory
The type of directory we're connected to.
Definition base.h:404
char const * version_str
As returned from the vendorVersion attribute in the rootDSE.
Definition base.h:204
trunk_t * trunk
Connection trunk.
Definition base.h:405
char const ** naming_contexts
Databases served by this directory.
Definition base.h:213
fr_ldap_directory_type_t type
Canonical server implementation.
Definition base.h:206
@ LDAP_PROC_SUCCESS
Operation was successful.
Definition base.h:585
Connection configuration.
Definition base.h:221
Tracks the state of a libldap connection handle.
Definition base.h:332
LDAP query structure.
Definition base.h:422
Thread LDAP trunk structure.
Definition base.h:399
fr_ldap_rcode_t fr_ldap_search_async(int *msgid, request_t *request, fr_ldap_connection_t *pconn, char const *dn, int scope, char const *filter, char const *const *attrs, LDAPControl **serverctrls, LDAPControl **clientctrls)
Search for something in the LDAP directory.
Definition base.c:529
fr_ldap_query_t * fr_ldap_search_alloc(TALLOC_CTX *ctx, char const *base_dn, int scope, char const *filter, char const *const *attrs, LDAPControl **serverctrls, LDAPControl **clientctrls)
Allocate a new search object.
Definition base.c:1027
static const conf_parser_t config[]
Definition base.c:183
#define WARN(fmt,...)
Definition radclient.h:47
#define INFO(fmt,...)
Definition radict.c:54
static char const * name
#define fr_table_str_by_value(_table, _number, _def)
Convert an integer to a string.
Definition table.h:772
An element in a lexicographically sorted array of name to num mappings.
Definition table.h:49
trunk_request_t * trunk_request_alloc(trunk_t *trunk, request_t *request)
(Pre-)Allocate a new trunk request
Definition trunk.c:2474
trunk_enqueue_t trunk_request_enqueue(trunk_request_t **treq_out, trunk_t *trunk, request_t *request, void *preq, void *rctx)
Enqueue a request that needs data written to the trunk.
Definition trunk.c:2587
Wraps a normal request.
Definition trunk.c:100