• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/router/samba-3.5.8/source4/auth/gensec/
1/*
2   Unix SMB/CIFS implementation.
3
4   Connect GENSEC to an external SASL lib
5
6   Copyright (C) Andrew Bartlett <abartlet@samba.org> 2006
7
8   This program is free software; you can redistribute it and/or modify
9   it under the terms of the GNU General Public License as published by
10   the Free Software Foundation; either version 3 of the License, or
11   (at your option) any later version.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18   You should have received a copy of the GNU General Public License
19   along with this program.  If not, see <http://www.gnu.org/licenses/>.
20*/
21
22#include "includes.h"
23#include "auth/auth.h"
24#include "auth/credentials/credentials.h"
25#include "auth/gensec/gensec.h"
26#include "auth/gensec/gensec_proto.h"
27#include "lib/socket/socket.h"
28#include <sasl/sasl.h>
29
30struct gensec_sasl_state {
31	sasl_conn_t *conn;
32	int step;
33};
34
35static NTSTATUS sasl_nt_status(int sasl_ret)
36{
37	switch (sasl_ret) {
38	case SASL_CONTINUE:
39		return NT_STATUS_MORE_PROCESSING_REQUIRED;
40	case SASL_NOMEM:
41		return NT_STATUS_NO_MEMORY;
42	case SASL_BADPARAM:
43	case SASL_NOMECH:
44		return NT_STATUS_INVALID_PARAMETER;
45	case SASL_BADMAC:
46		return NT_STATUS_ACCESS_DENIED;
47	case SASL_OK:
48		return NT_STATUS_OK;
49	default:
50		return NT_STATUS_UNSUCCESSFUL;
51	}
52}
53
54static int gensec_sasl_get_user(void *context, int id,
55				const char **result, unsigned *len)
56{
57	struct gensec_security *gensec_security = talloc_get_type(context, struct gensec_security);
58	const char *username = cli_credentials_get_username(gensec_get_credentials(gensec_security));
59	if (id != SASL_CB_USER && id != SASL_CB_AUTHNAME) {
60		return SASL_FAIL;
61	}
62
63	*result = username;
64	return SASL_OK;
65}
66
67static int gensec_sasl_get_realm(void *context, int id,
68				 const char **availrealms,
69				 const char **result)
70{
71	struct gensec_security *gensec_security = talloc_get_type(context, struct gensec_security);
72	const char *realm = cli_credentials_get_realm(gensec_get_credentials(gensec_security));
73	int i;
74	if (id != SASL_CB_GETREALM) {
75		return SASL_FAIL;
76	}
77
78	for (i=0; availrealms && availrealms[i]; i++) {
79		if (strcasecmp_m(realm, availrealms[i]) == 0) {
80			result[i] = availrealms[i];
81			return SASL_OK;
82		}
83	}
84	/* None of the realms match, so lets not specify one */
85	*result = "";
86	return SASL_OK;
87}
88
89static int gensec_sasl_get_password(sasl_conn_t *conn, void *context, int id,
90			     sasl_secret_t **psecret)
91{
92	struct gensec_security *gensec_security = talloc_get_type(context, struct gensec_security);
93	const char *password = cli_credentials_get_password(gensec_get_credentials(gensec_security));
94
95	sasl_secret_t *secret;
96	if (!password) {
97		*psecret = NULL;
98		return SASL_OK;
99	}
100	secret = talloc_size(gensec_security, sizeof(sasl_secret_t)+strlen(password));
101	if (!secret) {
102		return SASL_NOMEM;
103	}
104	secret->len = strlen(password);
105	safe_strcpy((char*)secret->data, password, secret->len+1);
106	*psecret = secret;
107	return SASL_OK;
108}
109
110static int gensec_sasl_dispose(struct gensec_sasl_state *gensec_sasl_state)
111{
112	sasl_dispose(&gensec_sasl_state->conn);
113	return SASL_OK;
114}
115
116static NTSTATUS gensec_sasl_client_start(struct gensec_security *gensec_security)
117{
118	struct gensec_sasl_state *gensec_sasl_state;
119	const char *service = gensec_get_target_service(gensec_security);
120	const char *target_name = gensec_get_target_hostname(gensec_security);
121	struct socket_address *local_socket_addr = gensec_get_my_addr(gensec_security);
122	struct socket_address *remote_socket_addr = gensec_get_peer_addr(gensec_security);
123	char *local_addr = NULL;
124	char *remote_addr = NULL;
125	int sasl_ret;
126
127	sasl_callback_t *callbacks;
128
129	gensec_sasl_state = talloc(gensec_security, struct gensec_sasl_state);
130	if (!gensec_sasl_state) {
131		return NT_STATUS_NO_MEMORY;
132	}
133
134	callbacks = talloc_array(gensec_sasl_state, sasl_callback_t, 5);
135	callbacks[0].id = SASL_CB_USER;
136	callbacks[0].proc = gensec_sasl_get_user;
137	callbacks[0].context = gensec_security;
138
139	callbacks[1].id =  SASL_CB_AUTHNAME;
140	callbacks[1].proc = gensec_sasl_get_user;
141	callbacks[1].context = gensec_security;
142
143	callbacks[2].id = SASL_CB_GETREALM;
144	callbacks[2].proc = gensec_sasl_get_realm;
145	callbacks[2].context = gensec_security;
146
147	callbacks[3].id = SASL_CB_PASS;
148	callbacks[3].proc = gensec_sasl_get_password;
149	callbacks[3].context = gensec_security;
150
151	callbacks[4].id = SASL_CB_LIST_END;
152	callbacks[4].proc = NULL;
153	callbacks[4].context = NULL;
154
155	gensec_security->private_data = gensec_sasl_state;
156
157	if (local_socket_addr) {
158		local_addr = talloc_asprintf(gensec_sasl_state,
159					     "%s;%d",
160					     local_socket_addr->addr,
161					     local_socket_addr->port);
162	}
163
164	if (remote_socket_addr) {
165		remote_addr = talloc_asprintf(gensec_sasl_state,
166					     "%s;%d",
167					     remote_socket_addr->addr,
168					     remote_socket_addr->port);
169	}
170	gensec_sasl_state->step = 0;
171
172	sasl_ret = sasl_client_new(service,
173				   target_name,
174				   local_addr, remote_addr, callbacks, 0,
175				   &gensec_sasl_state->conn);
176
177	if (sasl_ret == SASL_OK || sasl_ret == SASL_CONTINUE) {
178		sasl_security_properties_t props;
179		talloc_set_destructor(gensec_sasl_state, gensec_sasl_dispose);
180
181		ZERO_STRUCT(props);
182		if (gensec_security->want_features & GENSEC_FEATURE_SIGN) {
183			props.min_ssf = 1;
184		}
185		if (gensec_security->want_features & GENSEC_FEATURE_SEAL) {
186			props.min_ssf = 40;
187		}
188
189		props.max_ssf = UINT_MAX;
190		props.maxbufsize = 65536;
191		sasl_ret = sasl_setprop(gensec_sasl_state->conn, SASL_SEC_PROPS, &props);
192		if (sasl_ret != SASL_OK) {
193			return sasl_nt_status(sasl_ret);
194		}
195
196	} else {
197		DEBUG(1, ("GENSEC SASL: client_new failed: %s\n", sasl_errdetail(gensec_sasl_state->conn)));
198	}
199	return sasl_nt_status(sasl_ret);
200}
201
202static NTSTATUS gensec_sasl_update(struct gensec_security *gensec_security,
203				   TALLOC_CTX *out_mem_ctx,
204				   const DATA_BLOB in, DATA_BLOB *out)
205{
206	struct gensec_sasl_state *gensec_sasl_state = talloc_get_type(gensec_security->private_data,
207								      struct gensec_sasl_state);
208	int sasl_ret;
209	const char *out_data;
210	unsigned int out_len;
211
212	if (gensec_sasl_state->step == 0) {
213		const char *mech;
214		sasl_ret = sasl_client_start(gensec_sasl_state->conn, gensec_security->ops->sasl_name,
215					     NULL, &out_data, &out_len, &mech);
216	} else {
217		sasl_ret = sasl_client_step(gensec_sasl_state->conn,
218					    (char*)in.data, in.length, NULL,
219					    &out_data, &out_len);
220	}
221	if (sasl_ret == SASL_OK || sasl_ret == SASL_CONTINUE) {
222		*out = data_blob_talloc(out_mem_ctx, out_data, out_len);
223	} else {
224		DEBUG(1, ("GENSEC SASL: step %d update failed: %s\n", gensec_sasl_state->step,
225			  sasl_errdetail(gensec_sasl_state->conn)));
226	}
227	gensec_sasl_state->step++;
228	return sasl_nt_status(sasl_ret);
229}
230
231static NTSTATUS gensec_sasl_unwrap_packets(struct gensec_security *gensec_security,
232					TALLOC_CTX *out_mem_ctx,
233					const DATA_BLOB *in,
234					DATA_BLOB *out,
235					size_t *len_processed)
236{
237	struct gensec_sasl_state *gensec_sasl_state = talloc_get_type(gensec_security->private_data,
238								      struct gensec_sasl_state);
239	const char *out_data;
240	unsigned int out_len;
241
242	int sasl_ret = sasl_decode(gensec_sasl_state->conn,
243				   (char*)in->data, in->length, &out_data,
244				   &out_len);
245	if (sasl_ret == SASL_OK) {
246		*out = data_blob_talloc(out_mem_ctx, out_data, out_len);
247		*len_processed = in->length;
248	} else {
249		DEBUG(1, ("GENSEC SASL: unwrap failed: %s\n", sasl_errdetail(gensec_sasl_state->conn)));
250	}
251	return sasl_nt_status(sasl_ret);
252
253}
254
255static NTSTATUS gensec_sasl_wrap_packets(struct gensec_security *gensec_security,
256					TALLOC_CTX *out_mem_ctx,
257					const DATA_BLOB *in,
258					DATA_BLOB *out,
259					size_t *len_processed)
260{
261	struct gensec_sasl_state *gensec_sasl_state = talloc_get_type(gensec_security->private_data,
262								      struct gensec_sasl_state);
263	const char *out_data;
264	unsigned int out_len;
265
266	int sasl_ret = sasl_encode(gensec_sasl_state->conn,
267				   (char*)in->data, in->length, &out_data,
268				   &out_len);
269	if (sasl_ret == SASL_OK) {
270		*out = data_blob_talloc(out_mem_ctx, out_data, out_len);
271		*len_processed = in->length;
272	} else {
273		DEBUG(1, ("GENSEC SASL: wrap failed: %s\n", sasl_errdetail(gensec_sasl_state->conn)));
274	}
275	return sasl_nt_status(sasl_ret);
276}
277
278/* Try to figure out what features we actually got on the connection */
279static bool gensec_sasl_have_feature(struct gensec_security *gensec_security,
280				     uint32_t feature)
281{
282	struct gensec_sasl_state *gensec_sasl_state = talloc_get_type(gensec_security->private_data,
283								      struct gensec_sasl_state);
284	sasl_ssf_t ssf;
285	int sasl_ret = sasl_getprop(gensec_sasl_state->conn, SASL_SSF,
286			(const void**)&ssf);
287	if (sasl_ret != SASL_OK) {
288		return false;
289	}
290	if (feature & GENSEC_FEATURE_SIGN) {
291		if (ssf == 0) {
292			return false;
293		}
294		if (ssf >= 1) {
295			return true;
296		}
297	}
298	if (feature & GENSEC_FEATURE_SEAL) {
299		if (ssf <= 1) {
300			return false;
301		}
302		if (ssf > 1) {
303			return true;
304		}
305	}
306	return false;
307}
308
309/* This could in theory work with any SASL mech */
310static const struct gensec_security_ops gensec_sasl_security_ops = {
311	.name             = "sasl-DIGEST-MD5",
312	.sasl_name        = "DIGEST-MD5",
313	.client_start     = gensec_sasl_client_start,
314	.update 	  = gensec_sasl_update,
315	.wrap_packets     = gensec_sasl_wrap_packets,
316	.unwrap_packets   = gensec_sasl_unwrap_packets,
317	.have_feature     = gensec_sasl_have_feature,
318	.enabled          = true,
319	.priority         = GENSEC_SASL
320};
321
322static int gensec_sasl_log(void *context,
323		    int sasl_log_level,
324		    const char *message)
325{
326	int dl;
327	switch (sasl_log_level) {
328	case SASL_LOG_NONE:
329		dl = 0;
330		break;
331	case SASL_LOG_ERR:
332		dl = 1;
333		break;
334	case SASL_LOG_FAIL:
335		dl = 2;
336		break;
337	case SASL_LOG_WARN:
338		dl = 3;
339		break;
340	case SASL_LOG_NOTE:
341		dl = 5;
342		break;
343	case SASL_LOG_DEBUG:
344		dl = 10;
345		break;
346	case SASL_LOG_TRACE:
347		dl = 11;
348		break;
349#if DEBUG_PASSWORD
350	case SASL_LOG_PASS:
351		dl = 100;
352		break;
353#endif
354	default:
355		dl = 0;
356		break;
357	}
358	DEBUG(dl, ("gensec_sasl: %s\n", message));
359
360	return SASL_OK;
361}
362
363NTSTATUS gensec_sasl_init(void)
364{
365	NTSTATUS ret;
366	int sasl_ret;
367#if 0
368	int i;
369	const char **sasl_mechs;
370#endif
371
372	static const sasl_callback_t callbacks[] = {
373		{
374			.id = SASL_CB_LOG,
375			.proc = gensec_sasl_log,
376			.context = NULL,
377		},
378		{
379			.id = SASL_CB_LIST_END,
380			.proc = gensec_sasl_log,
381			.context = NULL,
382		}
383	};
384	sasl_ret = sasl_client_init(callbacks);
385
386	if (sasl_ret == SASL_NOMECH) {
387		/* Nothing to do here */
388		return NT_STATUS_OK;
389	}
390
391	if (sasl_ret != SASL_OK) {
392		return sasl_nt_status(sasl_ret);
393	}
394
395	/* For now, we just register DIGEST-MD5 */
396#if 1
397	ret = gensec_register(&gensec_sasl_security_ops);
398	if (!NT_STATUS_IS_OK(ret)) {
399		DEBUG(0,("Failed to register '%s' gensec backend!\n",
400			 gensec_sasl_security_ops.name));
401		return ret;
402	}
403#else
404	sasl_mechs = sasl_global_listmech();
405	for (i = 0; sasl_mechs && sasl_mechs[i]; i++) {
406		const struct gensec_security_ops *oldmech;
407		struct gensec_security_ops *newmech;
408		oldmech = gensec_security_by_sasl_name(NULL, sasl_mechs[i]);
409		if (oldmech) {
410			continue;
411		}
412		newmech = talloc(talloc_autofree_context(), struct gensec_security_ops);
413		if (!newmech) {
414			return NT_STATUS_NO_MEMORY;
415		}
416		*newmech = gensec_sasl_security_ops;
417		newmech->sasl_name = talloc_strdup(newmech, sasl_mechs[i]);
418		newmech->name = talloc_asprintf(newmech, "sasl-%s", sasl_mechs[i]);
419		if (!newmech->sasl_name || !newmech->name) {
420			return NT_STATUS_NO_MEMORY;
421		}
422
423		ret = gensec_register(newmech);
424		if (!NT_STATUS_IS_OK(ret)) {
425			DEBUG(0,("Failed to register '%s' gensec backend!\n",
426				 gensec_sasl_security_ops.name));
427			return ret;
428		}
429	}
430#endif
431	return NT_STATUS_OK;
432}
433