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_socket.h"
31 #include "auth/credentials/credentials.h"
32 #include "lib/stream/packet.h"
33 #include "param/param.h"
35 struct ldap_simple_creds
{
40 _PUBLIC_ NTSTATUS
ldap_rebind(struct ldap_connection
*conn
)
43 struct ldap_simple_creds
*creds
;
45 switch (conn
->bind
.type
) {
47 status
= ldap_bind_sasl(conn
, (struct cli_credentials
*)conn
->bind
.creds
,
51 case LDAP_BIND_SIMPLE
:
52 creds
= (struct ldap_simple_creds
*)conn
->bind
.creds
;
55 return NT_STATUS_UNSUCCESSFUL
;
58 status
= ldap_bind_simple(conn
, creds
->dn
, creds
->pw
);
62 return NT_STATUS_UNSUCCESSFUL
;
69 static struct ldap_message
*new_ldap_simple_bind_msg(struct ldap_connection
*conn
,
70 const char *dn
, const char *pw
)
72 struct ldap_message
*res
;
74 res
= new_ldap_message(conn
);
79 res
->type
= LDAP_TAG_BindRequest
;
80 res
->r
.BindRequest
.version
= 3;
81 res
->r
.BindRequest
.dn
= talloc_strdup(res
, dn
);
82 res
->r
.BindRequest
.mechanism
= LDAP_AUTH_MECH_SIMPLE
;
83 res
->r
.BindRequest
.creds
.password
= talloc_strdup(res
, pw
);
91 perform a simple username/password bind
93 _PUBLIC_ NTSTATUS
ldap_bind_simple(struct ldap_connection
*conn
,
94 const char *userdn
, const char *password
)
96 struct ldap_request
*req
;
97 struct ldap_message
*msg
;
102 return NT_STATUS_INVALID_CONNECTION
;
118 if (conn
->simple_pw
) {
119 pw
= conn
->simple_pw
;
125 msg
= new_ldap_simple_bind_msg(conn
, dn
, pw
);
126 NT_STATUS_HAVE_NO_MEMORY(msg
);
128 /* send the request */
129 req
= ldap_request_send(conn
, msg
);
131 NT_STATUS_HAVE_NO_MEMORY(req
);
133 /* wait for replies */
134 status
= ldap_request_wait(req
);
135 if (!NT_STATUS_IS_OK(status
)) {
140 /* check its a valid reply */
141 msg
= req
->replies
[0];
142 if (msg
->type
!= LDAP_TAG_BindResponse
) {
144 return NT_STATUS_UNEXPECTED_NETWORK_ERROR
;
147 status
= ldap_check_response(conn
, &msg
->r
.BindResponse
.response
);
151 if (NT_STATUS_IS_OK(status
)) {
152 struct ldap_simple_creds
*creds
= talloc(conn
, struct ldap_simple_creds
);
154 return NT_STATUS_NO_MEMORY
;
156 creds
->dn
= talloc_strdup(creds
, dn
);
157 creds
->pw
= talloc_strdup(creds
, pw
);
158 if (creds
->dn
== NULL
|| creds
->pw
== NULL
) {
159 return NT_STATUS_NO_MEMORY
;
161 conn
->bind
.type
= LDAP_BIND_SIMPLE
;
162 conn
->bind
.creds
= creds
;
169 static struct ldap_message
*new_ldap_sasl_bind_msg(struct ldap_connection
*conn
,
170 const char *sasl_mechanism
,
173 struct ldap_message
*res
;
175 res
= new_ldap_message(conn
);
180 res
->type
= LDAP_TAG_BindRequest
;
181 res
->r
.BindRequest
.version
= 3;
182 res
->r
.BindRequest
.dn
= "";
183 res
->r
.BindRequest
.mechanism
= LDAP_AUTH_MECH_SASL
;
184 res
->r
.BindRequest
.creds
.SASL
.mechanism
= talloc_strdup(res
, sasl_mechanism
);
186 res
->r
.BindRequest
.creds
.SASL
.secblob
= talloc(res
, DATA_BLOB
);
187 if (!res
->r
.BindRequest
.creds
.SASL
.secblob
) {
191 *res
->r
.BindRequest
.creds
.SASL
.secblob
= *secblob
;
193 res
->r
.BindRequest
.creds
.SASL
.secblob
= NULL
;
195 res
->controls
= NULL
;
202 perform a sasl bind using the given credentials
204 _PUBLIC_ NTSTATUS
ldap_bind_sasl(struct ldap_connection
*conn
,
205 struct cli_credentials
*creds
,
206 struct loadparm_context
*lp_ctx
)
209 TALLOC_CTX
*tmp_ctx
= NULL
;
211 DATA_BLOB input
= data_blob(NULL
, 0);
212 DATA_BLOB output
= data_blob(NULL
, 0);
214 struct ldap_message
**sasl_mechs_msgs
;
215 struct ldap_SearchResEntry
*search
;
218 const char **sasl_names
;
219 uint32_t old_gensec_features
;
220 static const char *supported_sasl_mech_attrs
[] = {
221 "supportedSASLMechanisms",
224 unsigned int logon_retries
= 0;
226 status
= ildap_search(conn
, "", LDAP_SEARCH_SCOPE_BASE
, "", supported_sasl_mech_attrs
,
227 false, NULL
, NULL
, &sasl_mechs_msgs
);
228 if (!NT_STATUS_IS_OK(status
)) {
229 DEBUG(1, ("Failed to inquire of target's available sasl mechs in rootdse search: %s\n",
234 count
= ildap_count_entries(conn
, sasl_mechs_msgs
);
236 DEBUG(1, ("Failed to inquire of target's available sasl mechs in rootdse search: wrong number of replies: %d\n",
241 tmp_ctx
= talloc_new(conn
);
242 if (tmp_ctx
== NULL
) goto failed
;
244 search
= &sasl_mechs_msgs
[0]->r
.SearchResultEntry
;
245 if (search
->num_attributes
!= 1) {
246 DEBUG(1, ("Failed to inquire of target's available sasl mechs in rootdse search: wrong number of attributes: %d != 1\n",
247 search
->num_attributes
));
251 sasl_names
= talloc_array(tmp_ctx
, const char *, search
->attributes
[0].num_values
+ 1);
253 DEBUG(1, ("talloc_arry(char *, %d) failed\n",
258 for (i
=0; i
<search
->attributes
[0].num_values
; i
++) {
259 sasl_names
[i
] = (const char *)search
->attributes
[0].values
[i
].data
;
261 sasl_names
[i
] = NULL
;
267 we loop back here on a logon failure, and re-create the
268 gensec session. The logon_retries counter ensures we don't
272 status
= gensec_client_start(conn
, &conn
->gensec
,
273 lpcfg_gensec_settings(conn
, lp_ctx
));
274 if (!NT_STATUS_IS_OK(status
)) {
275 DEBUG(0, ("Failed to start GENSEC engine (%s)\n", nt_errstr(status
)));
279 /* require Kerberos SIGN/SEAL only if we don't use SSL
280 * Windows seem not to like double encryption */
281 old_gensec_features
= cli_credentials_get_gensec_features(creds
);
282 if (tls_enabled(conn
->sock
)) {
283 cli_credentials_set_gensec_features(creds
, old_gensec_features
& ~(GENSEC_FEATURE_SIGN
|GENSEC_FEATURE_SEAL
));
286 /* this call also sets the gensec_want_features */
287 status
= gensec_set_credentials(conn
->gensec
, creds
);
288 if (!NT_STATUS_IS_OK(status
)) {
289 DEBUG(1, ("Failed to set GENSEC creds: %s\n",
294 /* reset the original gensec_features (on the credentials
295 * context, so we don't tatoo it ) */
296 cli_credentials_set_gensec_features(creds
, old_gensec_features
);
299 status
= gensec_set_target_hostname(conn
->gensec
, conn
->host
);
300 if (!NT_STATUS_IS_OK(status
)) {
301 DEBUG(1, ("Failed to set GENSEC target hostname: %s\n",
307 status
= gensec_set_target_service(conn
->gensec
, "ldap");
308 if (!NT_STATUS_IS_OK(status
)) {
309 DEBUG(1, ("Failed to set GENSEC target service: %s\n",
314 status
= gensec_start_mech_by_sasl_list(conn
->gensec
, sasl_names
);
315 if (!NT_STATUS_IS_OK(status
)) {
316 DEBUG(1, ("None of the %d proposed SASL mechs were acceptable: %s\n",
317 count
, nt_errstr(status
)));
322 NTSTATUS gensec_status
;
323 struct ldap_message
*response
;
324 struct ldap_message
*msg
;
325 struct ldap_request
*req
;
326 int result
= LDAP_OTHER
;
328 status
= gensec_update(conn
->gensec
, tmp_ctx
,
329 conn
->event
.event_ctx
,
332 /* The status value here, from GENSEC is vital to the security
333 * of the system. Even if the other end accepts, if GENSEC
334 * claims 'MORE_PROCESSING_REQUIRED' then you must keep
335 * feeding it blobs, or else the remote host/attacker might
336 * avoid mutal authentication requirements.
338 * Likewise, you must not feed GENSEC too much (after the OK),
339 * it doesn't like that either
342 gensec_status
= status
;
344 if (!NT_STATUS_EQUAL(status
, NT_STATUS_MORE_PROCESSING_REQUIRED
) &&
345 !NT_STATUS_IS_OK(status
)) {
348 if (NT_STATUS_IS_OK(status
) && output
.length
== 0) {
352 /* Perhaps we should make gensec_start_mech_by_sasl_list() return the name we got? */
353 msg
= new_ldap_sasl_bind_msg(tmp_ctx
, conn
->gensec
->ops
->sasl_name
, (output
.data
?&output
:NULL
));
355 status
= NT_STATUS_NO_MEMORY
;
359 req
= ldap_request_send(conn
, msg
);
361 status
= NT_STATUS_NO_MEMORY
;
364 talloc_reparent(conn
, tmp_ctx
, req
);
366 status
= ldap_result_n(req
, 0, &response
);
367 if (!NT_STATUS_IS_OK(status
)) {
371 if (response
->type
!= LDAP_TAG_BindResponse
) {
372 status
= NT_STATUS_UNEXPECTED_NETWORK_ERROR
;
376 result
= response
->r
.BindResponse
.response
.resultcode
;
378 if (result
== LDAP_INVALID_CREDENTIALS
) {
380 try a second time on invalid credentials, to
381 give the user a chance to re-enter the
382 password and to handle the case where our
383 kerberos ticket is invalid as the server
386 const char *principal
;
388 principal
= gensec_get_target_principal(conn
->gensec
);
389 if (principal
== NULL
) {
390 const char *hostname
= gensec_get_target_hostname(conn
->gensec
);
391 const char *service
= gensec_get_target_service(conn
->gensec
);
392 if (hostname
!= NULL
&& service
!= NULL
) {
393 principal
= talloc_asprintf(tmp_ctx
, "%s/%s", service
, hostname
);
397 if (cli_credentials_failed_kerberos_login(creds
, principal
, &logon_retries
) ||
398 cli_credentials_wrong_password(creds
)) {
400 destroy our gensec session and loop
401 back up to the top to retry,
402 offering the user a chance to enter
403 new credentials, or get a new ticket
406 talloc_free(conn
->gensec
);
408 goto try_logon_again
;
412 if (result
!= LDAP_SUCCESS
&& result
!= LDAP_SASL_BIND_IN_PROGRESS
) {
413 status
= ldap_check_response(conn
,
414 &response
->r
.BindResponse
.response
);
418 /* This is where we check if GENSEC wanted to be fed more data */
419 if (!NT_STATUS_EQUAL(gensec_status
, NT_STATUS_MORE_PROCESSING_REQUIRED
)) {
422 if (response
->r
.BindResponse
.SASL
.secblob
) {
423 input
= *response
->r
.BindResponse
.SASL
.secblob
;
425 input
= data_blob(NULL
, 0);
429 talloc_free(tmp_ctx
);
431 if (NT_STATUS_IS_OK(status
)) {
432 struct socket_context
*sasl_socket
;
433 status
= gensec_socket_init(conn
->gensec
,
436 conn
->event
.event_ctx
,
437 ldap_read_io_handler
,
440 if (!NT_STATUS_IS_OK(status
)) goto failed
;
442 conn
->sock
= sasl_socket
;
443 packet_set_socket(conn
->packet
, conn
->sock
);
445 conn
->bind
.type
= LDAP_BIND_SASL
;
446 conn
->bind
.creds
= creds
;
452 talloc_free(tmp_ctx
);
453 talloc_free(conn
->gensec
);