The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
control.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: b8d3d4188a5ab477d12609ecdcaa1a4c6ce69acd $
19  * @file lib/ldap/control.c
20  * @brief Functions for managing server/client side sort controls.
21  *
22  * @author Arran Cudbard-Bell (a.cudbardb@freeradius.org)
23  * @copyright 2015 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
24  */
25 RCSID("$Id: b8d3d4188a5ab477d12609ecdcaa1a4c6ce69acd $")
26 
28 
29 #include <freeradius-devel/ldap/base.h>
30 #ifdef LDAP_CONTROL_X_SESSION_TRACKING
31 #include <freeradius-devel/radius/radius.h>
32 #endif
33 
34 /** Merge connection and call specific client and server controls
35  *
36  * LDAP_OPT_CLIENT_CONTROLS and LDAP_OPT_SERVER_CONTROLS are useless
37  * because they're overridden in their entirety if any call specific
38  * controls are specified.
39  *
40  * @param[out] serverctrls_out Where to write serverctrls.
41  * @param[out] clientctrls_out Where to write clientctrls.
42  * @param[in] serverctrls_len length of serverctrls array.
43  * @param[in] clientctrls_len length of clientctrls array.
44  * @param[in] conn to get controls from.
45  * @param[in] serverctrls_in from arguments.
46  * @param[in] clientctrls_in from_arguments.
47  */
48  void fr_ldap_control_merge(LDAPControl *serverctrls_out[],
49  LDAPControl *clientctrls_out[],
50  size_t serverctrls_len,
51  size_t clientctrls_len,
53  LDAPControl *serverctrls_in[],
54  LDAPControl *clientctrls_in[])
55 {
56  size_t i, num_serverctrls = 0, num_clientctrls = 0;
57 
58  if (serverctrls_in) {
59  for (i = 0; serverctrls_in[i] && (num_serverctrls < LDAP_MAX_CONTROLS); i++) {
60  serverctrls_out[num_serverctrls++] = serverctrls_in[i];
61  }
62  }
63 
64  if (clientctrls_in) {
65  for (i = 0; clientctrls_in[i] && (num_clientctrls < LDAP_MAX_CONTROLS); i++) {
66  clientctrls_out[num_clientctrls++] = clientctrls_in[i];
67  }
68  }
69 
70  for (i = 0; (i < (size_t)conn->serverctrls_cnt) && (num_serverctrls < serverctrls_len); i++) {
71  serverctrls_out[num_serverctrls++] = conn->serverctrls[i].control;
72  }
73 
74  for (i = 0; (i < (size_t)conn->clientctrls_cnt) && (num_clientctrls < clientctrls_len); i++) {
75  clientctrls_out[num_clientctrls++] = conn->clientctrls[i].control;
76  }
77 
78  serverctrls_out[num_serverctrls] = NULL;
79  clientctrls_out[num_clientctrls] = NULL;
80 }
81 
82 /** Add a serverctrl to a connection handle
83  *
84  * All internal LDAP functions will pass this serverctrl to the server.
85  *
86  * @param conn to add control to.
87  * @param ctrl to add.
88  * @param freeit Whether the control should be freed when the handle is released or closed.
89  * @return
90  * - 0 on success.
91  * - -1 on failure (exceeded maximum controls).
92  */
93 int fr_ldap_control_add_server(fr_ldap_connection_t *conn, LDAPControl *ctrl, bool freeit)
94 {
95  if ((size_t)conn->serverctrls_cnt >= ((NUM_ELEMENTS(conn->serverctrls)) - 1)) {
96  return -1;
97  }
98 
99  conn->serverctrls[conn->serverctrls_cnt].control = ctrl;
100  conn->serverctrls[conn->serverctrls_cnt].freeit = freeit;
101  conn->serverctrls_cnt++;
102 
103  return 0;
104 }
105 
106 /** Add a clientctrl to a connection handle
107  *
108  * All internal LDAP functions will pass this clientctrl to libldap.
109  *
110  * @param conn to add control to.
111  * @param ctrl to add.
112  * @param freeit Whether the control should be freed when the handle is released or closed.
113  * @return
114  * - 0 on success.
115  * - -1 on failure (exceeded maximum controls).
116  */
117 int fr_ldap_control_add_client(fr_ldap_connection_t *conn, LDAPControl *ctrl, bool freeit)
118 {
119  if ((size_t)conn->clientctrls_cnt >= ((NUM_ELEMENTS(conn->clientctrls)) - 1)) {
120  return -1;
121  }
122 
123  conn->clientctrls[conn->clientctrls_cnt].control = ctrl;
124  conn->clientctrls[conn->clientctrls_cnt].freeit = freeit;
125  conn->clientctrls_cnt++;
126 
127  return 0;
128 }
129 
130 /** Clear and free any controls associated with a connection
131  *
132  * @param conn to clear controls from.
133  */
135 {
136  int i;
137 
138  for (i = 0; i < conn->serverctrls_cnt; i++) {
139  if (conn->serverctrls[i].freeit) ldap_control_free(conn->serverctrls[i].control);
140  conn->serverctrls[i].freeit = false;
141  conn->serverctrls[i].control = NULL;
142  }
143  conn->serverctrls_cnt = 0;
144 
145  for (i = 0; i < conn->clientctrls_cnt; i++) {
146  if (conn->clientctrls[i].freeit) ldap_control_free(conn->clientctrls[i].control);
147  conn->clientctrls[i].freeit = false;
148  conn->clientctrls[i].control = NULL;
149  }
150  conn->clientctrls_cnt = 0;
151 }
152 
153 #ifdef LDAP_CONTROL_X_SESSION_TRACKING
154 /** Add session controls to a connection as per draft-wahl-ldap-session
155  *
156  * @note the RFC states that the username identifier, must be the authenticated
157  * user id, not the purported one. As order of operations is configurable,
158  * we're going to leave that up to the server admin to satisfy that
159  * requirement
160  *
161  * For once the RFC is pretty helpful about what should be inserted into the
162  * various values, and maps out RADIUS attributes to formatOIDs, so none of
163  * this is configurable.
164  *
165  * @param conn to add controls to.
166  * @param request to draw attributes from.
167  */
169 {
170  /*
171  * The OpenLDAP guys didn't declare the formatOID parameter to
172  * ldap_create_session_tracking_control as const *sigh*.
173  */
174  static char username_oid[] = LDAP_CONTROL_X_SESSION_TRACKING_USERNAME;
175  static char acctsessionid_oid[] = LDAP_CONTROL_X_SESSION_TRACKING_RADIUS_ACCT_SESSION_ID;
176  static char acctmultisessionid_oid[] = LDAP_CONTROL_X_SESSION_TRACKING_RADIUS_ACCT_MULTI_SESSION_ID;
177 
178  int ret;
179 
180  char ipaddress[INET6_ADDRSTRLEN];
181  char *username = NULL;
182  char *acctsessionid = NULL;
183  char *acctmultisessionid = NULL;
184  char *hostname;
185 
186  LDAPControl *username_control = NULL;
187  LDAPControl *acctsessionid_control = NULL;
188  LDAPControl *acctmultisessionid_control = NULL;
189  struct berval tracking_id;
190 
191  fr_pair_t const *vp;
192 
193  memcpy(&hostname, main_config->name, sizeof(hostname)); /* const / non-const issues */
194 
195  /*
196  * @todo - MULTI_PROTOCOL - switch to auto-loaded dictionaries.
197  */
198  for (vp = fr_pair_list_head(&request->request_pairs);
199  vp;
200  vp = fr_pair_list_next(&request->request_pairs, vp)) {
201  if (fr_dict_attr_is_top_level(vp->da)) switch (vp->da->attr) {
202  case FR_NAS_IP_ADDRESS:
203  case FR_NAS_IPV6_ADDRESS:
204  fr_pair_print_value_quoted(&FR_SBUFF_OUT(ipaddress, sizeof(ipaddress)), vp, T_BARE_WORD);
205  break;
206 
207  case FR_USER_NAME:
208  memcpy(&username, &vp->vp_strvalue, sizeof(username));
209  break;
210 
211  case FR_ACCT_SESSION_ID:
212  memcpy(&acctsessionid, &vp->vp_strvalue, sizeof(acctsessionid));
213  break;
214 
215  case FR_ACCT_MULTI_SESSION_ID:
216  memcpy(&acctmultisessionid, &vp->vp_strvalue, sizeof(acctmultisessionid));
217  break;
218  }
219  }
220 
221  if (username) {
222  tracking_id.bv_val = username;
223  tracking_id.bv_len = talloc_array_length(username) - 1;
224 
225  ret = ldap_create_session_tracking_control(conn->handle, ipaddress,
226  hostname,
227  username_oid,
228  &tracking_id,
229  &username_control);
230  if (ret != LDAP_SUCCESS) {
231  REDEBUG("Failed creating username session tracking control: %s", ldap_err2string(ret));
232  error:
233  if (username_control) ldap_control_free(username_control);
234  if (acctsessionid_control) ldap_control_free(acctsessionid_control);
235  if (acctmultisessionid_control) ldap_control_free(acctmultisessionid_control);
236  return -1;
237  }
238  }
239 
240  if (acctsessionid) {
241  tracking_id.bv_val = acctsessionid;
242  tracking_id.bv_len = talloc_array_length(acctsessionid) - 1;
243 
244  ret = ldap_create_session_tracking_control(conn->handle, ipaddress,
245  hostname,
246  acctsessionid_oid,
247  &tracking_id,
248  &acctsessionid_control);
249  if (ret != LDAP_SUCCESS) {
250  REDEBUG("Failed creating acctsessionid session tracking control: %s", ldap_err2string(ret));
251  goto error;
252  }
253  }
254 
255  if (acctmultisessionid) {
256  tracking_id.bv_val = acctmultisessionid;
257  tracking_id.bv_len = talloc_array_length(acctmultisessionid) - 1;
258 
259  ret = ldap_create_session_tracking_control(conn->handle, ipaddress,
260  hostname,
261  acctmultisessionid_oid,
262  &tracking_id,
263  &acctmultisessionid_control);
264  if (ret != LDAP_SUCCESS) {
265  REDEBUG("Failed creating acctmultisessionid session tracking control: %s",
266  ldap_err2string(ret));
267  goto error;
268  }
269  }
270 
271  if ((conn->serverctrls_cnt + 3) >= LDAP_MAX_CONTROLS) {
272  REDEBUG("Insufficient space to add session tracking controls");
273  goto error;
274  }
275 
276  if (username_control && (fr_ldap_control_add_server(conn, username_control, true) < 0)) goto error;
277 
278  if (acctsessionid_control && (fr_ldap_control_add_server(conn, acctsessionid_control, true) < 0)) {
279  conn->serverctrls_cnt--;
280  conn->serverctrls[conn->serverctrls_cnt].control = NULL;
281  goto error;
282  }
283 
284  if (acctmultisessionid_control && (fr_ldap_control_add_server(conn, acctmultisessionid_control, true) < 0)) {
285  conn->serverctrls_cnt--;
286  conn->serverctrls[conn->serverctrls_cnt].control = NULL;
287  conn->serverctrls_cnt--;
288  conn->serverctrls[conn->serverctrls_cnt].control = NULL;
289  goto error;
290  }
291 
292  return 0;
293 }
294 #endif
295 
296 
#define USES_APPLE_DEPRECATED_API
Definition: build.h:468
#define RCSID(id)
Definition: build.h:481
#define NUM_ELEMENTS(_t)
Definition: build.h:335
static bool fr_dict_attr_is_top_level(fr_dict_attr_t const *da)
Return true if this attribute is parented directly off the dictionary root.
Definition: dict.h:754
int fr_ldap_control_add_session_tracking(fr_ldap_connection_t *conn, request_t *request)
LDAP * handle
libldap handle.
Definition: base.h:333
int serverctrls_cnt
Number of server controls associated with the handle.
Definition: base.h:339
bool freeit
Whether the control should be freed after we've finished using it.
Definition: base.h:136
fr_ldap_control_t clientctrls[LDAP_MAX_CONTROLS+1]
Client controls to use for all operations with this handle.
Definition: base.h:337
int clientctrls_cnt
Number of client controls associated with the handle.
Definition: base.h:340
#define LDAP_MAX_CONTROLS
Maximum number of client/server controls.
Definition: base.h:94
LDAPControl * control
LDAP control.
Definition: base.h:135
fr_ldap_control_t serverctrls[LDAP_MAX_CONTROLS+1]
Server controls to use for all operations with this handle.
Definition: base.h:335
Tracks the state of a libldap connection handle.
Definition: base.h:332
void fr_ldap_control_clear(fr_ldap_connection_t *conn)
Clear and free any controls associated with a connection.
Definition: control.c:134
int fr_ldap_control_add_server(fr_ldap_connection_t *conn, LDAPControl *ctrl, bool freeit)
Add a serverctrl to a connection handle.
Definition: control.c:93
int fr_ldap_control_add_client(fr_ldap_connection_t *conn, LDAPControl *ctrl, bool freeit)
Add a clientctrl to a connection handle.
Definition: control.c:117
USES_APPLE_DEPRECATED_API void fr_ldap_control_merge(LDAPControl *serverctrls_out[], LDAPControl *clientctrls_out[], size_t serverctrls_len, size_t clientctrls_len, fr_ldap_connection_t *conn, LDAPControl *serverctrls_in[], LDAPControl *clientctrls_in[])
Merge connection and call specific client and server controls.
Definition: control.c:48
main_config_t const * main_config
Main server configuration.
Definition: main_config.c:69
char const * name
Name of the daemon, usually 'radiusd'.
Definition: main_config.h:52
unsigned long int size_t
Definition: merged_model.c:25
#define REDEBUG(fmt,...)
Definition: radclient.h:52
static char const * hostname(char *buf, size_t buflen, uint32_t ipaddr)
Definition: radwho.c:133
username
Definition: rlm_securid.c:420
#define FR_SBUFF_OUT(_start, _len_or_end)
fr_pair_t * vp
Stores an attribute, a value and various bits of other data.
Definition: pair.h:68
fr_dict_attr_t const *_CONST da
Dictionary attribute defines the attribute number, vendor and type of the pair.
Definition: pair.h:69
@ T_BARE_WORD
Definition: token.h:120
fr_pair_t * fr_pair_list_head(fr_pair_list_t const *list)
Get the head of a valuepair list.
Definition: pair_inline.c:43
fr_pair_t * fr_pair_list_next(fr_pair_list_t const *list, fr_pair_t const *item))
Get the next item in a valuepair list after a specific entry.
Definition: pair_inline.c:70
ssize_t fr_pair_print_value_quoted(fr_sbuff_t *out, fr_pair_t const *vp, fr_token_t quote)
Print the value of an attribute to a string.
Definition: pair_print.c:40