The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
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  */
27 RCSID("$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")) {
104  directory->type = FR_LDAP_DIRECTORY_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")) {
114  directory->type = FR_LDAP_DIRECTORY_UNBOUND_ID;
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")) {
124  directory->type = FR_LDAP_DIRECTORY_SIEMENS_AG;
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 
173 found:
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  */
239 static 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:468
#define RCSID(id)
Definition: build.h:481
#define L(_str)
Helper for initialising arrays of string literals.
Definition: build.h:207
#define NUM_ELEMENTS(_t)
Definition: build.h:335
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.
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
fr_ldap_config_t const * config
rlm_ldap connection configuration.
Definition: base.h:344
char * fr_ldap_berval_to_string(TALLOC_CTX *ctx, struct berval const *in)
Convert a berval to a talloced string.
Definition: util.c:390
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:528
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:1026
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_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:2575
trunk_request_t * trunk_request_alloc(trunk_t *trunk, request_t *request)
(Pre-)Allocate a new trunk request
Definition: trunk.c:2462
Wraps a normal request.
Definition: trunk.c:97