2 Unix SMB/CIFS mplementation.
6 Copyright (C) Andrew Tridgell 2005
7 Copyright (C) Volker Lendecke 2004
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
25 #include "libcli/ldap/libcli_ldap.h"
26 #include "libcli/ldap/ldap_proto.h"
27 #include "libcli/ldap/ldap_client.h"
28 #include "lib/tls/tls.h"
29 #include "auth/gensec/gensec.h"
30 #include "auth/gensec/gensec_internal.h" /* TODO: remove this */
31 #include "source4/auth/gensec/gensec_tstream.h"
32 #include "auth/credentials/credentials.h"
33 #include "lib/stream/packet.h"
34 #include "param/param.h"
36 struct ldap_simple_creds
{
41 _PUBLIC_ NTSTATUS
ldap_rebind(struct ldap_connection
*conn
)
44 struct ldap_simple_creds
*creds
;
46 switch (conn
->bind
.type
) {
48 status
= ldap_bind_sasl(conn
, (struct cli_credentials
*)conn
->bind
.creds
,
52 case LDAP_BIND_SIMPLE
:
53 creds
= (struct ldap_simple_creds
*)conn
->bind
.creds
;
56 return NT_STATUS_UNSUCCESSFUL
;
59 status
= ldap_bind_simple(conn
, creds
->dn
, creds
->pw
);
63 return NT_STATUS_UNSUCCESSFUL
;
70 static struct ldap_message
*new_ldap_simple_bind_msg(struct ldap_connection
*conn
,
71 const char *dn
, const char *pw
)
73 struct ldap_message
*res
;
75 res
= new_ldap_message(conn
);
80 res
->type
= LDAP_TAG_BindRequest
;
81 res
->r
.BindRequest
.version
= 3;
82 res
->r
.BindRequest
.dn
= talloc_strdup(res
, dn
);
83 res
->r
.BindRequest
.mechanism
= LDAP_AUTH_MECH_SIMPLE
;
84 res
->r
.BindRequest
.creds
.password
= talloc_strdup(res
, pw
);
92 perform a simple username/password bind
94 _PUBLIC_ NTSTATUS
ldap_bind_simple(struct ldap_connection
*conn
,
95 const char *userdn
, const char *password
)
97 struct ldap_request
*req
;
98 struct ldap_message
*msg
;
103 return NT_STATUS_INVALID_CONNECTION
;
119 if (conn
->simple_pw
) {
120 pw
= conn
->simple_pw
;
126 msg
= new_ldap_simple_bind_msg(conn
, dn
, pw
);
127 NT_STATUS_HAVE_NO_MEMORY(msg
);
129 /* send the request */
130 req
= ldap_request_send(conn
, msg
);
132 NT_STATUS_HAVE_NO_MEMORY(req
);
134 /* wait for replies */
135 status
= ldap_request_wait(req
);
136 if (!NT_STATUS_IS_OK(status
)) {
141 /* check its a valid reply */
142 msg
= req
->replies
[0];
143 if (msg
->type
!= LDAP_TAG_BindResponse
) {
145 return NT_STATUS_UNEXPECTED_NETWORK_ERROR
;
148 status
= ldap_check_response(conn
, &msg
->r
.BindResponse
.response
);
152 if (NT_STATUS_IS_OK(status
)) {
153 struct ldap_simple_creds
*creds
= talloc(conn
, struct ldap_simple_creds
);
155 return NT_STATUS_NO_MEMORY
;
157 creds
->dn
= talloc_strdup(creds
, dn
);
158 creds
->pw
= talloc_strdup(creds
, pw
);
159 if (creds
->dn
== NULL
|| creds
->pw
== NULL
) {
160 return NT_STATUS_NO_MEMORY
;
162 conn
->bind
.type
= LDAP_BIND_SIMPLE
;
163 conn
->bind
.creds
= creds
;
170 static struct ldap_message
*new_ldap_sasl_bind_msg(struct ldap_connection
*conn
,
171 const char *sasl_mechanism
,
174 struct ldap_message
*res
;
176 res
= new_ldap_message(conn
);
181 res
->type
= LDAP_TAG_BindRequest
;
182 res
->r
.BindRequest
.version
= 3;
183 res
->r
.BindRequest
.dn
= "";
184 res
->r
.BindRequest
.mechanism
= LDAP_AUTH_MECH_SASL
;
185 res
->r
.BindRequest
.creds
.SASL
.mechanism
= talloc_strdup(res
, sasl_mechanism
);
187 res
->r
.BindRequest
.creds
.SASL
.secblob
= talloc(res
, DATA_BLOB
);
188 if (!res
->r
.BindRequest
.creds
.SASL
.secblob
) {
192 *res
->r
.BindRequest
.creds
.SASL
.secblob
= *secblob
;
194 res
->r
.BindRequest
.creds
.SASL
.secblob
= NULL
;
196 res
->controls
= NULL
;
203 perform a sasl bind using the given credentials
205 _PUBLIC_ NTSTATUS
ldap_bind_sasl(struct ldap_connection
*conn
,
206 struct cli_credentials
*creds
,
207 struct loadparm_context
*lp_ctx
)
210 TALLOC_CTX
*tmp_ctx
= NULL
;
212 DATA_BLOB input
= data_blob(NULL
, 0);
213 DATA_BLOB output
= data_blob(NULL
, 0);
215 struct ldap_message
**sasl_mechs_msgs
;
216 struct ldap_SearchResEntry
*search
;
220 const char **sasl_names
;
221 uint32_t old_gensec_features
;
222 static const char *supported_sasl_mech_attrs
[] = {
223 "supportedSASLMechanisms",
226 unsigned int logon_retries
= 0;
229 if (conn
->sockets
.active
== NULL
) {
230 status
= NT_STATUS_CONNECTION_DISCONNECTED
;
234 queue_length
= tevent_queue_length(conn
->sockets
.send_queue
);
235 if (queue_length
!= 0) {
236 status
= NT_STATUS_INVALID_PARAMETER_MIX
;
237 DEBUG(1, ("SASL bind triggered with non empty send_queue[%zu]: %s\n",
238 queue_length
, nt_errstr(status
)));
242 if (conn
->pending
!= NULL
) {
243 status
= NT_STATUS_INVALID_PARAMETER_MIX
;
244 DEBUG(1, ("SASL bind triggered with pending requests: %s\n",
249 status
= ildap_search(conn
, "", LDAP_SEARCH_SCOPE_BASE
, "", supported_sasl_mech_attrs
,
250 false, NULL
, NULL
, &sasl_mechs_msgs
);
251 if (!NT_STATUS_IS_OK(status
)) {
252 DEBUG(1, ("Failed to inquire of target's available sasl mechs in rootdse search: %s\n",
257 count
= ildap_count_entries(conn
, sasl_mechs_msgs
);
259 DEBUG(1, ("Failed to inquire of target's available sasl mechs in rootdse search: wrong number of replies: %d\n",
264 tmp_ctx
= talloc_new(conn
);
265 if (tmp_ctx
== NULL
) goto failed
;
267 search
= &sasl_mechs_msgs
[0]->r
.SearchResultEntry
;
268 if (search
->num_attributes
!= 1) {
269 DEBUG(1, ("Failed to inquire of target's available sasl mechs in rootdse search: wrong number of attributes: %d != 1\n",
270 search
->num_attributes
));
274 sasl_names
= talloc_array(tmp_ctx
, const char *, search
->attributes
[0].num_values
+ 1);
276 DEBUG(1, ("talloc_arry(char *, %d) failed\n",
281 for (i
=0; i
<search
->attributes
[0].num_values
; i
++) {
282 sasl_names
[i
] = (const char *)search
->attributes
[0].values
[i
].data
;
284 sasl_names
[i
] = NULL
;
290 we loop back here on a logon failure, and re-create the
291 gensec session. The logon_retries counter ensures we don't
295 status
= gensec_client_start(conn
, &conn
->gensec
,
296 lpcfg_gensec_settings(conn
, lp_ctx
));
297 if (!NT_STATUS_IS_OK(status
)) {
298 DEBUG(0, ("Failed to start GENSEC engine (%s)\n", nt_errstr(status
)));
302 /* require Kerberos SIGN/SEAL only if we don't use SSL
303 * Windows seem not to like double encryption */
304 old_gensec_features
= cli_credentials_get_gensec_features(creds
);
305 if (conn
->sockets
.active
== conn
->sockets
.tls
) {
306 cli_credentials_set_gensec_features(creds
, old_gensec_features
& ~(GENSEC_FEATURE_SIGN
|GENSEC_FEATURE_SEAL
));
309 /* this call also sets the gensec_want_features */
310 status
= gensec_set_credentials(conn
->gensec
, creds
);
311 if (!NT_STATUS_IS_OK(status
)) {
312 DEBUG(1, ("Failed to set GENSEC creds: %s\n",
317 /* reset the original gensec_features (on the credentials
318 * context, so we don't tatoo it ) */
319 cli_credentials_set_gensec_features(creds
, old_gensec_features
);
322 status
= gensec_set_target_hostname(conn
->gensec
, conn
->host
);
323 if (!NT_STATUS_IS_OK(status
)) {
324 DEBUG(1, ("Failed to set GENSEC target hostname: %s\n",
330 status
= gensec_set_target_service(conn
->gensec
, "ldap");
331 if (!NT_STATUS_IS_OK(status
)) {
332 DEBUG(1, ("Failed to set GENSEC target service: %s\n",
337 status
= gensec_start_mech_by_sasl_list(conn
->gensec
, sasl_names
);
338 if (!NT_STATUS_IS_OK(status
)) {
339 DEBUG(1, ("None of the %d proposed SASL mechs were acceptable: %s\n",
340 count
, nt_errstr(status
)));
345 NTSTATUS gensec_status
;
346 struct ldap_message
*response
;
347 struct ldap_message
*msg
;
348 struct ldap_request
*req
;
349 int result
= LDAP_OTHER
;
351 status
= gensec_update_ev(conn
->gensec
, tmp_ctx
,
352 conn
->event
.event_ctx
,
355 /* The status value here, from GENSEC is vital to the security
356 * of the system. Even if the other end accepts, if GENSEC
357 * claims 'MORE_PROCESSING_REQUIRED' then you must keep
358 * feeding it blobs, or else the remote host/attacker might
359 * avoid mutal authentication requirements.
361 * Likewise, you must not feed GENSEC too much (after the OK),
362 * it doesn't like that either.
364 * For SASL/EXTERNAL, there is no data to send, but we still
365 * must send the actual Bind request the first time around.
366 * Otherwise, a result of NT_STATUS_OK with 0 output means the
367 * end of a multi-step authentication, and no message must be
371 gensec_status
= status
;
373 if (!NT_STATUS_EQUAL(status
, NT_STATUS_MORE_PROCESSING_REQUIRED
) &&
374 !NT_STATUS_IS_OK(status
)) {
377 if (NT_STATUS_IS_OK(status
) && output
.length
== 0) {
383 /* Perhaps we should make gensec_start_mech_by_sasl_list() return the name we got? */
384 msg
= new_ldap_sasl_bind_msg(tmp_ctx
, conn
->gensec
->ops
->sasl_name
, (output
.data
?&output
:NULL
));
386 status
= NT_STATUS_NO_MEMORY
;
390 req
= ldap_request_send(conn
, msg
);
392 status
= NT_STATUS_NO_MEMORY
;
395 talloc_reparent(conn
, tmp_ctx
, req
);
397 status
= ldap_result_n(req
, 0, &response
);
398 if (!NT_STATUS_IS_OK(status
)) {
402 if (response
->type
!= LDAP_TAG_BindResponse
) {
403 status
= NT_STATUS_UNEXPECTED_NETWORK_ERROR
;
407 result
= response
->r
.BindResponse
.response
.resultcode
;
409 if (result
== LDAP_INVALID_CREDENTIALS
) {
411 try a second time on invalid credentials, to
412 give the user a chance to re-enter the
413 password and to handle the case where our
414 kerberos ticket is invalid as the server
417 const char *principal
;
419 principal
= gensec_get_target_principal(conn
->gensec
);
420 if (principal
== NULL
) {
421 const char *hostname
= gensec_get_target_hostname(conn
->gensec
);
422 const char *service
= gensec_get_target_service(conn
->gensec
);
423 if (hostname
!= NULL
&& service
!= NULL
) {
424 principal
= talloc_asprintf(tmp_ctx
, "%s/%s", service
, hostname
);
428 if (cli_credentials_failed_kerberos_login(creds
, principal
, &logon_retries
) ||
429 cli_credentials_wrong_password(creds
)) {
431 destroy our gensec session and loop
432 back up to the top to retry,
433 offering the user a chance to enter
434 new credentials, or get a new ticket
437 talloc_free(conn
->gensec
);
439 goto try_logon_again
;
443 if (result
!= LDAP_SUCCESS
&& result
!= LDAP_SASL_BIND_IN_PROGRESS
) {
444 status
= ldap_check_response(conn
,
445 &response
->r
.BindResponse
.response
);
449 /* This is where we check if GENSEC wanted to be fed more data */
450 if (!NT_STATUS_EQUAL(gensec_status
, NT_STATUS_MORE_PROCESSING_REQUIRED
)) {
453 if (response
->r
.BindResponse
.SASL
.secblob
) {
454 input
= *response
->r
.BindResponse
.SASL
.secblob
;
456 input
= data_blob(NULL
, 0);
460 TALLOC_FREE(tmp_ctx
);
462 if (!NT_STATUS_IS_OK(status
)) {
466 conn
->bind
.type
= LDAP_BIND_SASL
;
467 conn
->bind
.creds
= creds
;
469 if (!gensec_have_feature(conn
->gensec
, GENSEC_FEATURE_SIGN
) &&
470 !gensec_have_feature(conn
->gensec
, GENSEC_FEATURE_SEAL
)) {
474 status
= gensec_create_tstream(conn
->sockets
.raw
,
477 &conn
->sockets
.sasl
);
478 if (!NT_STATUS_IS_OK(status
)) {
482 conn
->sockets
.active
= conn
->sockets
.sasl
;
487 talloc_free(tmp_ctx
);
488 talloc_free(conn
->gensec
);