2 Unix SMB/CIFS implementation.
4 Copyright (C) Andrew Tridgell 2001
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include "../libcli/auth/spnego.h"
22 #include "auth/credentials/credentials.h"
23 #include "auth/gensec/gensec.h"
24 #include "auth_generic.h"
27 #include "system/gssapi.h"
28 #include "lib/param/loadparm.h"
30 #include "lib/util/asn1.h"
34 static ADS_STATUS
ads_sasl_gensec_wrap(struct ads_saslwrap
*wrap
,
35 uint8_t *buf
, uint32_t len
)
37 struct gensec_security
*gensec_security
=
38 talloc_get_type_abort(wrap
->wrap_private_data
,
39 struct gensec_security
);
41 DATA_BLOB unwrapped
, wrapped
;
42 TALLOC_CTX
*frame
= talloc_stackframe();
44 unwrapped
= data_blob_const(buf
, len
);
46 nt_status
= gensec_wrap(gensec_security
, frame
, &unwrapped
, &wrapped
);
47 if (!NT_STATUS_IS_OK(nt_status
)) {
49 return ADS_ERROR_NT(nt_status
);
52 if ((wrap
->out
.size
- 4) < wrapped
.length
) {
54 return ADS_ERROR_NT(NT_STATUS_INTERNAL_ERROR
);
57 /* copy the wrapped blob to the right location */
58 memcpy(wrap
->out
.buf
+ 4, wrapped
.data
, wrapped
.length
);
60 /* set how many bytes must be written to the underlying socket */
61 wrap
->out
.left
= 4 + wrapped
.length
;
68 static ADS_STATUS
ads_sasl_gensec_unwrap(struct ads_saslwrap
*wrap
)
70 struct gensec_security
*gensec_security
=
71 talloc_get_type_abort(wrap
->wrap_private_data
,
72 struct gensec_security
);
74 DATA_BLOB unwrapped
, wrapped
;
75 TALLOC_CTX
*frame
= talloc_stackframe();
77 wrapped
= data_blob_const(wrap
->in
.buf
+ 4, wrap
->in
.ofs
- 4);
79 nt_status
= gensec_unwrap(gensec_security
, frame
, &wrapped
, &unwrapped
);
80 if (!NT_STATUS_IS_OK(nt_status
)) {
82 return ADS_ERROR_NT(nt_status
);
85 if (wrapped
.length
< unwrapped
.length
) {
87 return ADS_ERROR_NT(NT_STATUS_INTERNAL_ERROR
);
90 /* copy the wrapped blob to the right location */
91 memcpy(wrap
->in
.buf
+ 4, unwrapped
.data
, unwrapped
.length
);
93 /* set how many bytes must be written to the underlying socket */
94 wrap
->in
.left
= unwrapped
.length
;
102 static void ads_sasl_gensec_disconnect(struct ads_saslwrap
*wrap
)
104 struct gensec_security
*gensec_security
=
105 talloc_get_type_abort(wrap
->wrap_private_data
,
106 struct gensec_security
);
108 TALLOC_FREE(gensec_security
);
110 wrap
->wrap_ops
= NULL
;
111 wrap
->wrap_private_data
= NULL
;
114 static const struct ads_saslwrap_ops ads_sasl_gensec_ops
= {
116 .wrap
= ads_sasl_gensec_wrap
,
117 .unwrap
= ads_sasl_gensec_unwrap
,
118 .disconnect
= ads_sasl_gensec_disconnect
122 perform a LDAP/SASL/SPNEGO/{NTLMSSP,KRB5} bind (just how many layers can
123 we fit on one socket??)
125 static ADS_STATUS
ads_sasl_spnego_gensec_bind(ADS_STRUCT
*ads
,
127 enum credentials_use_kerberos krb5_state
,
128 const char *target_service
,
129 const char *target_hostname
,
130 const DATA_BLOB server_blob
)
132 DATA_BLOB blob_in
= data_blob_null
;
133 DATA_BLOB blob_out
= data_blob_null
;
137 struct auth_generic_state
*auth_generic_state
;
138 bool use_spnego_principal
= lp_client_use_spnego_principal();
139 const char *sasl_list
[] = { sasl
, NULL
};
141 struct ads_saslwrap
*wrap
= &ads
->ldap_wrap_data
;
143 nt_status
= auth_generic_client_prepare(NULL
, &auth_generic_state
);
144 if (!NT_STATUS_IS_OK(nt_status
)) {
145 return ADS_ERROR_NT(nt_status
);
148 if (!NT_STATUS_IS_OK(nt_status
= auth_generic_set_username(auth_generic_state
, ads
->auth
.user_name
))) {
149 return ADS_ERROR_NT(nt_status
);
151 if (!NT_STATUS_IS_OK(nt_status
= auth_generic_set_domain(auth_generic_state
, ads
->auth
.realm
))) {
152 return ADS_ERROR_NT(nt_status
);
154 if (!NT_STATUS_IS_OK(nt_status
= auth_generic_set_password(auth_generic_state
, ads
->auth
.password
))) {
155 return ADS_ERROR_NT(nt_status
);
158 if (server_blob
.length
== 0) {
159 use_spnego_principal
= false;
162 if (krb5_state
== CRED_USE_KERBEROS_DISABLED
) {
163 use_spnego_principal
= false;
166 cli_credentials_set_kerberos_state(auth_generic_state
->credentials
,
170 if (target_service
!= NULL
) {
171 nt_status
= gensec_set_target_service(
172 auth_generic_state
->gensec_security
,
174 if (!NT_STATUS_IS_OK(nt_status
)) {
175 return ADS_ERROR_NT(nt_status
);
179 if (target_hostname
!= NULL
) {
180 nt_status
= gensec_set_target_hostname(
181 auth_generic_state
->gensec_security
,
183 if (!NT_STATUS_IS_OK(nt_status
)) {
184 return ADS_ERROR_NT(nt_status
);
188 if (target_service
!= NULL
&& target_hostname
!= NULL
) {
189 use_spnego_principal
= false;
192 switch (wrap
->wrap_type
) {
193 case ADS_SASLWRAP_TYPE_SEAL
:
194 gensec_want_feature(auth_generic_state
->gensec_security
, GENSEC_FEATURE_SIGN
);
195 gensec_want_feature(auth_generic_state
->gensec_security
, GENSEC_FEATURE_SEAL
);
197 case ADS_SASLWRAP_TYPE_SIGN
:
198 if (ads
->auth
.flags
& ADS_AUTH_SASL_FORCE
) {
199 gensec_want_feature(auth_generic_state
->gensec_security
, GENSEC_FEATURE_SIGN
);
202 * windows servers are broken with sign only,
203 * so we let the NTLMSSP backend to seal here,
204 * via GENSEC_FEATURE_LDAP_STYLE.
206 gensec_want_feature(auth_generic_state
->gensec_security
, GENSEC_FEATURE_SIGN
);
207 gensec_want_feature(auth_generic_state
->gensec_security
, GENSEC_FEATURE_LDAP_STYLE
);
210 case ADS_SASLWRAP_TYPE_PLAIN
:
214 nt_status
= auth_generic_client_start_by_sasl(auth_generic_state
,
216 if (!NT_STATUS_IS_OK(nt_status
)) {
217 return ADS_ERROR_NT(nt_status
);
220 rc
= LDAP_SASL_BIND_IN_PROGRESS
;
221 if (use_spnego_principal
) {
222 blob_in
= data_blob_dup_talloc(talloc_tos(), server_blob
);
223 if (blob_in
.length
== 0) {
224 TALLOC_FREE(auth_generic_state
);
225 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
228 blob_in
= data_blob_null
;
230 blob_out
= data_blob_null
;
233 struct berval cred
, *scred
= NULL
;
235 nt_status
= gensec_update(auth_generic_state
->gensec_security
,
236 talloc_tos(), blob_in
, &blob_out
);
237 data_blob_free(&blob_in
);
238 if (!NT_STATUS_EQUAL(nt_status
, NT_STATUS_MORE_PROCESSING_REQUIRED
)
239 && !NT_STATUS_IS_OK(nt_status
))
241 TALLOC_FREE(auth_generic_state
);
242 data_blob_free(&blob_out
);
243 return ADS_ERROR_NT(nt_status
);
246 if (NT_STATUS_IS_OK(nt_status
) && rc
== 0 && blob_out
.length
== 0) {
250 cred
.bv_val
= (char *)blob_out
.data
;
251 cred
.bv_len
= blob_out
.length
;
253 rc
= ldap_sasl_bind_s(ads
->ldap
.ld
, NULL
, sasl
, &cred
, NULL
, NULL
, &scred
);
254 data_blob_free(&blob_out
);
255 if ((rc
!= LDAP_SASL_BIND_IN_PROGRESS
) && (rc
!= 0)) {
260 TALLOC_FREE(auth_generic_state
);
261 return ADS_ERROR(rc
);
264 blob_in
= data_blob_talloc(talloc_tos(),
267 if (blob_in
.length
!= scred
->bv_len
) {
269 TALLOC_FREE(auth_generic_state
);
270 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
274 blob_in
= data_blob_null
;
276 if (NT_STATUS_IS_OK(nt_status
) && rc
== 0 && blob_in
.length
== 0) {
281 data_blob_free(&blob_in
);
282 data_blob_free(&blob_out
);
284 if (wrap
->wrap_type
>= ADS_SASLWRAP_TYPE_SEAL
) {
287 ok
= gensec_have_feature(auth_generic_state
->gensec_security
,
288 GENSEC_FEATURE_SEAL
);
290 DEBUG(0,("The gensec feature sealing request, but unavailable\n"));
291 TALLOC_FREE(auth_generic_state
);
292 return ADS_ERROR_NT(NT_STATUS_INVALID_NETWORK_RESPONSE
);
295 ok
= gensec_have_feature(auth_generic_state
->gensec_security
,
296 GENSEC_FEATURE_SIGN
);
298 DEBUG(0,("The gensec feature signing request, but unavailable\n"));
299 TALLOC_FREE(auth_generic_state
);
300 return ADS_ERROR_NT(NT_STATUS_INVALID_NETWORK_RESPONSE
);
303 } else if (wrap
->wrap_type
>= ADS_SASLWRAP_TYPE_SIGN
) {
306 ok
= gensec_have_feature(auth_generic_state
->gensec_security
,
307 GENSEC_FEATURE_SIGN
);
309 DEBUG(0,("The gensec feature signing request, but unavailable\n"));
310 TALLOC_FREE(auth_generic_state
);
311 return ADS_ERROR_NT(NT_STATUS_INVALID_NETWORK_RESPONSE
);
315 ads
->auth
.tgs_expire
= LONG_MAX
;
316 end_nt_time
= gensec_expire_time(auth_generic_state
->gensec_security
);
317 if (end_nt_time
!= GENSEC_EXPIRE_TIME_INFINITY
) {
319 nttime_to_timeval(&tv
, end_nt_time
);
320 ads
->auth
.tgs_expire
= tv
.tv_sec
;
323 if (wrap
->wrap_type
> ADS_SASLWRAP_TYPE_PLAIN
) {
325 gensec_max_wrapped_size(auth_generic_state
->gensec_security
);
326 wrap
->out
.max_unwrapped
=
327 gensec_max_input_size(auth_generic_state
->gensec_security
);
329 wrap
->out
.sig_size
= max_wrapped
- wrap
->out
.max_unwrapped
;
331 * Note that we have to truncate this to 0x2C
332 * (taken from a capture with LDAP unbind), as the
333 * signature size is not constant for Kerberos with
336 wrap
->in
.min_wrapped
= MIN(wrap
->out
.sig_size
, 0x2C);
337 wrap
->in
.max_wrapped
= ADS_SASL_WRAPPING_IN_MAX_WRAPPED
;
338 status
= ads_setup_sasl_wrapping(wrap
, ads
->ldap
.ld
,
339 &ads_sasl_gensec_ops
,
340 auth_generic_state
->gensec_security
);
341 if (!ADS_ERR_OK(status
)) {
342 DEBUG(0, ("ads_setup_sasl_wrapping() failed: %s\n",
343 ads_errstr(status
)));
344 TALLOC_FREE(auth_generic_state
);
347 /* Only keep the gensec_security element around long-term */
348 talloc_steal(NULL
, auth_generic_state
->gensec_security
);
350 TALLOC_FREE(auth_generic_state
);
352 return ADS_ERROR(rc
);
356 struct ads_service_principal
{
362 static void ads_free_service_principal(struct ads_service_principal
*p
)
364 SAFE_FREE(p
->service
);
365 SAFE_FREE(p
->hostname
);
366 SAFE_FREE(p
->string
);
370 static ADS_STATUS
ads_guess_target(ADS_STRUCT
*ads
,
375 ADS_STATUS status
= ADS_ERROR(LDAP_NO_MEMORY
);
382 frame
= talloc_stackframe();
384 return ADS_ERROR(LDAP_NO_MEMORY
);
387 if (ads
->server
.realm
&& ads
->server
.ldap_server
) {
388 server
= strlower_talloc(frame
, ads
->server
.ldap_server
);
389 if (server
== NULL
) {
393 realm
= strupper_talloc(frame
, ads
->server
.realm
);
399 * If we got a name which is bigger than a NetBIOS name,
400 * but isn't a FQDN, create one.
402 if (strlen(server
) > 15 && strstr(server
, ".") == NULL
) {
405 dnsdomain
= strlower_talloc(frame
, ads
->server
.realm
);
406 if (dnsdomain
== NULL
) {
410 server
= talloc_asprintf(frame
,
413 if (server
== NULL
) {
417 } else if (ads
->config
.realm
&& ads
->config
.ldap_server_name
) {
418 server
= strlower_talloc(frame
, ads
->config
.ldap_server_name
);
419 if (server
== NULL
) {
423 realm
= strupper_talloc(frame
, ads
->config
.realm
);
429 * If we got a name which is bigger than a NetBIOS name,
430 * but isn't a FQDN, create one.
432 if (strlen(server
) > 15 && strstr(server
, ".") == NULL
) {
435 dnsdomain
= strlower_talloc(frame
, ads
->server
.realm
);
436 if (dnsdomain
== NULL
) {
440 server
= talloc_asprintf(frame
,
443 if (server
== NULL
) {
449 if (server
== NULL
|| realm
== NULL
) {
453 *service
= SMB_STRDUP("ldap");
454 if (*service
== NULL
) {
455 status
= ADS_ERROR(LDAP_PARAM_ERROR
);
458 *hostname
= SMB_STRDUP(server
);
459 if (*hostname
== NULL
) {
461 status
= ADS_ERROR(LDAP_PARAM_ERROR
);
464 rc
= asprintf(&princ
, "ldap/%s@%s", server
, realm
);
465 if (rc
== -1 || princ
== NULL
) {
467 SAFE_FREE(*hostname
);
468 status
= ADS_ERROR(LDAP_PARAM_ERROR
);
474 status
= ADS_SUCCESS
;
480 static ADS_STATUS
ads_generate_service_principal(ADS_STRUCT
*ads
,
481 struct ads_service_principal
*p
)
487 status
= ads_guess_target(ads
,
491 if (!ADS_ERR_OK(status
)) {
498 #endif /* HAVE_KRB5 */
501 parse a negTokenInit packet giving a GUID, a list of supported
502 OIDs (the mechanisms) and a principal name string
504 static bool spnego_parse_negTokenInit(TALLOC_CTX
*ctx
,
506 char *OIDs
[ASN1_MAX_OIDS
],
514 for (i
= 0; i
< ASN1_MAX_OIDS
; i
++) {
522 *secblob
= data_blob_null
;
525 data
= asn1_init(talloc_tos(), ASN1_MAX_TREE_DEPTH
);
530 if (!asn1_load(data
, blob
)) goto err
;
532 if (!asn1_start_tag(data
,ASN1_APPLICATION(0))) goto err
;
534 if (!asn1_check_OID(data
,OID_SPNEGO
)) goto err
;
536 /* negTokenInit [0] NegTokenInit */
537 if (!asn1_start_tag(data
,ASN1_CONTEXT(0))) goto err
;
538 if (!asn1_start_tag(data
,ASN1_SEQUENCE(0))) goto err
;
540 /* mechTypes [0] MechTypeList OPTIONAL */
542 /* Not really optional, we depend on this to decide
543 * what mechanisms we have to work with. */
545 if (!asn1_start_tag(data
,ASN1_CONTEXT(0))) goto err
;
546 if (!asn1_start_tag(data
,ASN1_SEQUENCE(0))) goto err
;
547 for (i
=0; asn1_tag_remaining(data
) > 0 && i
< ASN1_MAX_OIDS
-1; i
++) {
548 if (!asn1_read_OID(data
,ctx
, &OIDs
[i
])) {
551 if (asn1_has_error(data
)) {
556 if (!asn1_end_tag(data
)) goto err
;
557 if (!asn1_end_tag(data
)) goto err
;
560 Win7 + Live Sign-in Assistant attaches a mechToken
561 ASN1_CONTEXT(2) to the negTokenInit packet
562 which breaks our negotiation if we just assume
563 the next tag is ASN1_CONTEXT(3).
566 if (asn1_peek_tag(data
, ASN1_CONTEXT(1))) {
569 /* reqFlags [1] ContextFlags OPTIONAL */
570 if (!asn1_start_tag(data
, ASN1_CONTEXT(1))) goto err
;
571 if (!asn1_start_tag(data
, ASN1_BIT_STRING
)) goto err
;
572 while (asn1_tag_remaining(data
) > 0) {
573 if (!asn1_read_uint8(data
, &flags
)) goto err
;
575 if (!asn1_end_tag(data
)) goto err
;
576 if (!asn1_end_tag(data
)) goto err
;
579 if (asn1_peek_tag(data
, ASN1_CONTEXT(2))) {
580 DATA_BLOB sblob
= data_blob_null
;
581 /* mechToken [2] OCTET STRING OPTIONAL */
582 if (!asn1_start_tag(data
, ASN1_CONTEXT(2))) goto err
;
583 if (!asn1_read_OctetString(data
, ctx
, &sblob
)) goto err
;
584 if (!asn1_end_tag(data
)) {
585 data_blob_free(&sblob
);
591 data_blob_free(&sblob
);
595 if (asn1_peek_tag(data
, ASN1_CONTEXT(3))) {
597 /* mechListMIC [3] OCTET STRING OPTIONAL */
598 if (!asn1_start_tag(data
, ASN1_CONTEXT(3))) goto err
;
599 if (!asn1_start_tag(data
, ASN1_SEQUENCE(0))) goto err
;
600 if (!asn1_start_tag(data
, ASN1_CONTEXT(0))) goto err
;
601 if (!asn1_read_GeneralString(data
, ctx
, &princ
)) goto err
;
602 if (!asn1_end_tag(data
)) goto err
;
603 if (!asn1_end_tag(data
)) goto err
;
604 if (!asn1_end_tag(data
)) goto err
;
612 if (!asn1_end_tag(data
)) goto err
;
613 if (!asn1_end_tag(data
)) goto err
;
615 if (!asn1_end_tag(data
)) goto err
;
617 ret
= !asn1_has_error(data
);
621 if (asn1_has_error(data
)) {
624 TALLOC_FREE(*principal
);
627 data_blob_free(secblob
);
629 for(j
= 0; j
< i
&& j
< ASN1_MAX_OIDS
-1; j
++) {
630 TALLOC_FREE(OIDs
[j
]);
639 this performs a SASL/SPNEGO bind
641 static ADS_STATUS
ads_sasl_spnego_bind(ADS_STRUCT
*ads
)
643 TALLOC_CTX
*frame
= talloc_stackframe();
644 struct ads_service_principal p
= {0};
645 struct berval
*scred
=NULL
;
648 DATA_BLOB blob
= data_blob_null
;
649 char *given_principal
= NULL
;
650 char *OIDs
[ASN1_MAX_OIDS
];
652 bool got_kerberos_mechanism
= False
;
654 const char *mech
= NULL
;
656 rc
= ldap_sasl_bind_s(ads
->ldap
.ld
, NULL
, "GSS-SPNEGO", NULL
, NULL
, NULL
, &scred
);
658 if (rc
!= LDAP_SASL_BIND_IN_PROGRESS
) {
659 status
= ADS_ERROR(rc
);
663 blob
= data_blob(scred
->bv_val
, scred
->bv_len
);
668 file_save("sasl_spnego.dat", blob
.data
, blob
.length
);
671 /* the server sent us the first part of the SPNEGO exchange in the negprot
673 if (!spnego_parse_negTokenInit(talloc_tos(), blob
, OIDs
, &given_principal
, NULL
) ||
675 status
= ADS_ERROR(LDAP_OPERATIONS_ERROR
);
678 TALLOC_FREE(given_principal
);
680 /* make sure the server understands kerberos */
681 for (i
=0;OIDs
[i
];i
++) {
682 DEBUG(3,("ads_sasl_spnego_bind: got OID=%s\n", OIDs
[i
]));
684 if (strcmp(OIDs
[i
], OID_KERBEROS5_OLD
) == 0 ||
685 strcmp(OIDs
[i
], OID_KERBEROS5
) == 0) {
686 got_kerberos_mechanism
= True
;
689 talloc_free(OIDs
[i
]);
692 status
= ads_generate_service_principal(ads
, &p
);
693 if (!ADS_ERR_OK(status
)) {
698 if (!(ads
->auth
.flags
& ADS_AUTH_DISABLE_KERBEROS
) &&
699 got_kerberos_mechanism
)
703 if (ads
->auth
.password
== NULL
||
704 ads
->auth
.password
[0] == '\0')
707 status
= ads_sasl_spnego_gensec_bind(ads
, "GSS-SPNEGO",
708 CRED_USE_KERBEROS_REQUIRED
,
709 p
.service
, p
.hostname
,
711 if (ADS_ERR_OK(status
)) {
712 ads_free_service_principal(&p
);
716 DEBUG(10,("ads_sasl_spnego_gensec_bind(KRB5) failed with: %s, "
717 "calling kinit\n", ads_errstr(status
)));
720 status
= ADS_ERROR_KRB5(ads_kinit_password(ads
));
722 if (ADS_ERR_OK(status
)) {
723 status
= ads_sasl_spnego_gensec_bind(ads
, "GSS-SPNEGO",
724 CRED_USE_KERBEROS_REQUIRED
,
725 p
.service
, p
.hostname
,
727 if (!ADS_ERR_OK(status
)) {
728 DBG_ERR("kinit succeeded but "
729 "SPNEGO bind with Kerberos failed "
730 "for %s/%s - user[%s], realm[%s]: %s\n",
731 p
.service
, p
.hostname
,
738 /* only fallback to NTLMSSP if allowed */
739 if (ADS_ERR_OK(status
) ||
740 !(ads
->auth
.flags
& ADS_AUTH_ALLOW_NTLMSSP
)) {
744 DBG_WARNING("SASL bind with Kerberos failed "
745 "for %s/%s - user[%s], realm[%s]: %s, "
746 "try to fallback to NTLMSSP\n",
747 p
.service
, p
.hostname
,
754 /* lets do NTLMSSP ... this has the big advantage that we don't need
755 to sync clocks, and we don't rely on special versions of the krb5
756 library for HMAC_MD4 encryption */
759 if (!(ads
->auth
.flags
& ADS_AUTH_ALLOW_NTLMSSP
)) {
760 DBG_WARNING("We can't use NTLMSSP, it is not allowed.\n");
761 status
= ADS_ERROR_NT(NT_STATUS_NETWORK_CREDENTIAL_CONFLICT
);
765 if (lp_weak_crypto() == SAMBA_WEAK_CRYPTO_DISALLOWED
) {
766 DBG_WARNING("We can't fallback to NTLMSSP, weak crypto is"
768 status
= ADS_ERROR_NT(NT_STATUS_NETWORK_CREDENTIAL_CONFLICT
);
772 status
= ads_sasl_spnego_gensec_bind(ads
, "GSS-SPNEGO",
773 CRED_USE_KERBEROS_DISABLED
,
774 p
.service
, p
.hostname
,
777 if (!ADS_ERR_OK(status
)) {
778 DEBUG(1,("ads_sasl_spnego_gensec_bind(%s) failed "
779 "for %s/%s with user[%s] realm=[%s]: %s\n", mech
,
780 p
.service
, p
.hostname
,
783 ads_errstr(status
)));
785 ads_free_service_principal(&p
);
787 if (blob
.data
!= NULL
) {
788 data_blob_free(&blob
);
793 /* mapping between SASL mechanisms and functions */
796 ADS_STATUS (*fn
)(ADS_STRUCT
*);
797 } sasl_mechanisms
[] = {
798 {"GSS-SPNEGO", ads_sasl_spnego_bind
},
802 ADS_STATUS
ads_sasl_bind(ADS_STRUCT
*ads
)
804 const char *attrs
[] = {"supportedSASLMechanisms", NULL
};
809 struct ads_saslwrap
*wrap
= &ads
->ldap_wrap_data
;
811 /* get a list of supported SASL mechanisms */
812 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
813 if (!ADS_ERR_OK(status
)) return status
;
815 values
= ldap_get_values(ads
->ldap
.ld
, res
, "supportedSASLMechanisms");
817 if (ads
->auth
.flags
& ADS_AUTH_SASL_SEAL
) {
818 wrap
->wrap_type
= ADS_SASLWRAP_TYPE_SEAL
;
819 } else if (ads
->auth
.flags
& ADS_AUTH_SASL_SIGN
) {
820 wrap
->wrap_type
= ADS_SASLWRAP_TYPE_SIGN
;
822 wrap
->wrap_type
= ADS_SASLWRAP_TYPE_PLAIN
;
825 /* try our supported mechanisms in order */
826 for (i
=0;sasl_mechanisms
[i
].name
;i
++) {
827 /* see if the server supports it */
828 for (j
=0;values
&& values
[j
];j
++) {
829 if (strcmp(values
[j
], sasl_mechanisms
[i
].name
) == 0) {
830 DEBUG(4,("Found SASL mechanism %s\n", values
[j
]));
832 status
= sasl_mechanisms
[i
].fn(ads
);
833 if (status
.error_type
== ENUM_ADS_ERROR_LDAP
&&
834 status
.err
.rc
== LDAP_STRONG_AUTH_REQUIRED
&&
835 wrap
->wrap_type
== ADS_SASLWRAP_TYPE_PLAIN
)
837 DEBUG(3,("SASL bin got LDAP_STRONG_AUTH_REQUIRED "
838 "retrying with signing enabled\n"));
839 wrap
->wrap_type
= ADS_SASLWRAP_TYPE_SIGN
;
842 ldap_value_free(values
);
849 ldap_value_free(values
);
851 return ADS_ERROR(LDAP_AUTH_METHOD_NOT_SUPPORTED
);
854 #endif /* HAVE_LDAP */