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: 9af4bde1933d9f774159825f74f98209204f5805 $
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: 9af4bde1933d9f774159825f74f98209204f5805 $")
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("Siemens AG"), FR_LDAP_DIRECTORY_SIEMENS_AG },
44  { L("Sun One Directory"), FR_LDAP_DIRECTORY_SUN_ONE_DIRECTORY },
45  { L("Unbound ID"), FR_LDAP_DIRECTORY_UNBOUND_ID },
46  { L("Unknown"), FR_LDAP_DIRECTORY_UNKNOWN },
47  { L("eDirectory"), FR_LDAP_DIRECTORY_EDIRECTORY }
48 };
50 
52  LDAPMessage *result, char const *name)
53 {
54  int entry_cnt, i, num, ldap_errno;
55  LDAPMessage *entry;
56  struct berval **values = NULL;
57 
58  entry_cnt = ldap_count_entries(handle, result);
59  if (entry_cnt != 1) {
60  WARN("Capability check failed: Ambiguous result for rootDSE, expected 1 entry, got %i", entry_cnt);
61  return 1;
62  }
63 
64  entry = ldap_first_entry(handle, result);
65  if (!entry) {
66  ldap_get_option(handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
67 
68  WARN("Capability check failed: Failed retrieving entry: %s", ldap_err2string(ldap_errno));
69  return 1;
70  }
71 
72  values = ldap_get_values_len(handle, entry, "vendorname");
73  if (values) {
74  directory->vendor_str = fr_ldap_berval_to_string(directory, values[0]);
75  INFO("Directory vendor: %s", directory->vendor_str);
76 
77  ldap_value_free_len(values);
78  }
79 
80  values = ldap_get_values_len(handle, entry, "vendorversion");
81  if (values) {
82  directory->version_str = fr_ldap_berval_to_string(directory, values[0]);
83  INFO("Directory version: %s", directory->version_str);
84 
85  ldap_value_free_len(values);
86  }
87 
88  if (directory->vendor_str) {
89  if (strcasestr(directory->vendor_str, "International Business Machines")) {
90  directory->type = FR_LDAP_DIRECTORY_IBM;
91  }
92 
93  goto found;
94  }
95 
96  if (directory->version_str) {
97  /*
98  * Novell eDirectory vendorversion contains eDirectory
99  */
100  if (strcasestr(directory->version_str, "eDirectory")) {
101  directory->type = FR_LDAP_DIRECTORY_EDIRECTORY;
102  /*
103  * Oracle unified directory vendorversion contains Oracle Unified Directory
104  */
105  } else if (strcasestr(directory->version_str, "Oracle Unified Directory")) {
107  /*
108  * Unbound directory vendorversion contains UnboundID
109  */
110  } else if (strcasestr(directory->version_str, "UnboundID")) {
111  directory->type = FR_LDAP_DIRECTORY_UNBOUND_ID;
112  /*
113  * NetScape directory venderversion contains Netscape-Directory
114  */
115  } else if (strcasestr(directory->version_str, "Netscape-Directory")) {
116  directory->type = FR_LDAP_DIRECTORY_NETSCAPE;
117  /*
118  * Siemens AG directory vendorversion contains DirX Directory
119  */
120  } else if (strcasestr(directory->version_str, "DirX Directory")) {
121  directory->type = FR_LDAP_DIRECTORY_SIEMENS_AG;
122  /*
123  * Sun One Directory vendorversion contains Sun Java
124  */
125  } else if (strcasestr(directory->version_str, "Sun Java")) {
127  }
128  goto found;
129  }
130 
131  /*
132  * isGlobalCatalogReady is only present on ActiveDirectory
133  * instances. AD doesn't provide vendorname or vendorversion
134  */
135  values = ldap_get_values_len(handle, entry, "isGlobalCatalogReady");
136  if (values) {
138  ldap_value_free_len(values);
139  goto found;
140  }
141 
142  /*
143  * OpenLDAP has a special objectClass for its RootDSE
144  */
145  values = ldap_get_values_len(handle, entry, "objectClass");
146  if (values) {
147  num = ldap_count_values_len(values);
148  for (i = 0; i < num; i++) {
149  if (strncmp("OpenLDAProotDSE", values[i]->bv_val, values[i]->bv_len) == 0) {
150  directory->type = FR_LDAP_DIRECTORY_OPENLDAP;
151  }
152  }
153  ldap_value_free_len(values);
154  goto found;
155  }
156 
157  /*
158  * Oracle Virtual Directory and Oracle Internet Directory
159  */
160  values = ldap_get_values_len(handle, entry, "orcldirectoryversion");
161  if (values) {
162  if (memmem(values[0]->bv_val, values[0]->bv_len, "OID", 3)) {
164  } else if (memmem(values[0]->bv_val, values[0]->bv_len, "OVD", 3)) {
166  }
167  ldap_value_free_len(values);
168  }
169 
170 found:
171  INFO("Directory type: %s", fr_table_str_by_value(fr_ldap_directory_type_table, directory->type, "<INVALID>"));
172 
173  switch (directory->type) {
176  directory->cleartext_password = false;
177  break;
178 
179  default:
180  directory->cleartext_password = true;
181  break;
182  }
183 
184  /*
185  * Evaluate what type of sync the directory supports
186  */
187  values = ldap_get_values_len(handle, entry, "supportedControl");
188  if (values) {
189  num = ldap_count_values_len(values);
190  for (i = 0; i < num; i++) {
191  if (strncmp(LDAP_CONTROL_SYNC, values[i]->bv_val, values[i]->bv_len) == 0) {
192  INFO("Directory supports RFC 4533");
193  directory->sync_type = FR_LDAP_SYNC_RFC4533;
194  break;
195  }
196  if (strncmp(LDAP_SERVER_NOTIFICATION_OID, values[i]->bv_val, values[i]->bv_len) == 0) {
197  INFO("Directory supports LDAP_SERVER_NOTIFICATION_OID");
199  break;
200  }
201  if (strncmp(LDAP_CONTROL_PERSIST_REQUEST, values[i]->bv_val, values[i]->bv_len) == 0) {
202  INFO("Directory supports persistent search");
204  break;
205  }
206  }
207  ldap_value_free_len(values);
208  } else {
209  WARN("No supportedControl returned by LDAP server");
210  }
211 
212  /*
213  * Extract naming contexts
214  */
215  values = ldap_get_values_len(handle, entry, "namingContexts");
216  if (!values) return 0;
217 
218  num = ldap_count_values_len(values);
219  directory->naming_contexts = talloc_array(directory, char const *, num);
220  for (i = 0; i < num; i++) {
221  directory->naming_contexts[i] = fr_ldap_berval_to_string(directory, values[i]);
222  }
223  ldap_value_free_len(values);
224 
225  return 0;
226 }
227 
228 /** Parse results of search on rootDSE to gather data on LDAP server
229  *
230  * @param[in] handle on which the query was run.
231  * @param[in] query which requested the rootDSE.
232  * @param[in] result head of LDAP results message chain.
233  * @param[in] rctx LDAP directory whose properties are to be populated.
234  */
235 static void ldap_trunk_directory_alloc_read(LDAP *handle, fr_ldap_query_t *query, LDAPMessage *result, void *rctx)
236 {
237  fr_ldap_config_t const *config = query->ldap_conn->config;
238  fr_ldap_directory_t *directory = talloc_get_type_abort(rctx, fr_ldap_directory_t);
239 
240  (void)fr_ldap_directory_result_parse(directory, handle, result, config->name);
241 }
242 
243 /** Async extract useful information from the rootDSE of the LDAP server
244  *
245  * This is called once for each new thread trunk when it first connects.
246  *
247  * @param[in] ctx to allocate fr_ldap_directory_t in.
248  * @param[in] ttrunk Thread trunk connection to be queried
249  * @return
250  * - 0 on success
251  * < 0 on failure
252  */
254 {
255  fr_ldap_query_t *query;
256  static char const *attrs[] = LDAP_DIRECTORY_ATTRS;
257  fr_trunk_request_t *treq;
258 
259  ttrunk->directory = talloc_zero(ctx, fr_ldap_directory_t);
260  if (!ttrunk->directory) return -1;
261 
262  treq = fr_trunk_request_alloc(ttrunk->trunk, NULL);
263  if (!treq) return -1;
264 
265  query = fr_ldap_search_alloc(treq, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, NULL, NULL);
267  query->treq = treq;
268 
269  fr_trunk_request_enqueue(&query->treq, ttrunk->trunk, NULL, query, ttrunk->directory);
270 
271  return 0;
272 }
273 
274 /** Async extract useful information from the rootDSE of the LDAP server
275  *
276  * This version is for a single connection rather than a connection trunk
277  *
278  * @param[in] ldap_conn connection to be queried
279  * @return
280  * - message ID on success
281  * < 0 on failure
282  */
284 {
285  int msgid;
286  static char const *attrs[] = LDAP_DIRECTORY_ATTRS;
287 
288  ldap_conn->directory = talloc_zero(ldap_conn, fr_ldap_directory_t);
289  if (!ldap_conn->directory) return -1;
290 
291  if (fr_ldap_search_async(&msgid, NULL, ldap_conn, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs,
292  NULL, NULL) != LDAP_PROC_SUCCESS) return -1;
293 
294  return msgid;
295 }
#define USES_APPLE_DEPRECATED_API
Definition: build.h:431
#define RCSID(id)
Definition: build.h:444
#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:283
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:253
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:235
int fr_ldap_directory_result_parse(fr_ldap_directory_t *directory, LDAP *handle, LDAPMessage *result, char const *name)
Definition: directory.c:51
static size_t fr_ldap_directory_type_table_len
Definition: directory.c:49
#define LDAP_DIRECTORY_ATTRS
Definition: base.h:815
#define LDAP_SERVER_NOTIFICATION_OID
OID of Active Directory control for.
Definition: base.h:115
fr_trunk_request_t * treq
Trunk request this query is associated with.
Definition: base.h:451
@ 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_sync_type_t sync_type
What kind of LDAP sync this directory supports.
Definition: base.h:209
fr_ldap_result_parser_t parser
Custom results parser.
Definition: base.h:461
fr_ldap_directory_t * directory
The type of directory we're connected to.
Definition: base.h:340
fr_ldap_config_t const * config
rlm_ldap connection configuration.
Definition: base.h:342
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:200
fr_ldap_connection_t * ldap_conn
LDAP connection this query is running on.
Definition: base.h:452
bool cleartext_password
Whether the server will return the user's plaintext password.
Definition: base.h:206
@ FR_LDAP_SYNC_ACTIVE_DIRECTORY
Directory supports AD style persistent search.
Definition: base.h:159
@ FR_LDAP_SYNC_PERSISTENT_SEARCH
Directory supports persistent search.
Definition: base.h:160
@ FR_LDAP_SYNC_RFC4533
Directory supports RFC 4533.
Definition: base.h:158
fr_ldap_directory_t * directory
The type of directory we're connected to.
Definition: base.h:402
fr_trunk_t * trunk
Connection trunk.
Definition: base.h:403
char const * version_str
As returned from the vendorVersion attribute in the rootDSE.
Definition: base.h:202
char const ** naming_contexts
Databases served by this directory.
Definition: base.h:211
fr_ldap_directory_type_t type
Canonical server implementation.
Definition: base.h:204
@ LDAP_PROC_SUCCESS
Operation was successful.
Definition: base.h:580
Connection configuration.
Definition: base.h:219
Tracks the state of a libldap connection handle.
Definition: base.h:330
LDAP query structure.
Definition: base.h:420
Thread LDAP trunk structure.
Definition: base.h:397
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:972
static const conf_parser_t config[]
Definition: base.c:188
#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:253
An element in a lexicographically sorted array of name to num mappings.
Definition: table.h:45
fr_trunk_request_t * fr_trunk_request_alloc(fr_trunk_t *trunk, request_t *request)
(Pre-)Allocate a new trunk request
Definition: trunk.c:2369
fr_trunk_enqueue_t fr_trunk_request_enqueue(fr_trunk_request_t **treq_out, fr_trunk_t *trunk, request_t *request, void *preq, void *rctx)
Enqueue a request that needs data written to the trunk.
Definition: trunk.c:2481
Wraps a normal request.
Definition: trunk.c:97