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 2 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, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 perform a LDAP/SASL/SPNEGO/NTLMSSP bind (just how many layers can
27 we fit on one socket??)
29 static ADS_STATUS
ads_sasl_spnego_ntlmssp_bind(ADS_STRUCT
*ads
)
31 DATA_BLOB msg1
= data_blob(NULL
, 0);
32 DATA_BLOB blob
= data_blob(NULL
, 0);
33 DATA_BLOB blob_in
= data_blob(NULL
, 0);
34 DATA_BLOB blob_out
= data_blob(NULL
, 0);
35 struct berval cred
, *scred
= NULL
;
40 struct ntlmssp_state
*ntlmssp_state
;
42 if (!NT_STATUS_IS_OK(nt_status
= ntlmssp_client_start(&ntlmssp_state
))) {
43 return ADS_ERROR_NT(nt_status
);
45 ntlmssp_state
->neg_flags
&= ~NTLMSSP_NEGOTIATE_SIGN
;
47 if (!NT_STATUS_IS_OK(nt_status
= ntlmssp_set_username(ntlmssp_state
, ads
->auth
.user_name
))) {
48 return ADS_ERROR_NT(nt_status
);
50 if (!NT_STATUS_IS_OK(nt_status
= ntlmssp_set_domain(ntlmssp_state
, ads
->auth
.realm
))) {
51 return ADS_ERROR_NT(nt_status
);
53 if (!NT_STATUS_IS_OK(nt_status
= ntlmssp_set_password(ntlmssp_state
, ads
->auth
.password
))) {
54 return ADS_ERROR_NT(nt_status
);
57 blob_in
= data_blob(NULL
, 0);
60 nt_status
= ntlmssp_update(ntlmssp_state
,
62 data_blob_free(&blob_in
);
63 if ((NT_STATUS_EQUAL(nt_status
, NT_STATUS_MORE_PROCESSING_REQUIRED
)
64 || NT_STATUS_IS_OK(nt_status
))
67 /* and wrap it in a SPNEGO wrapper */
68 msg1
= gen_negTokenInit(OID_NTLMSSP
, blob_out
);
70 /* wrap it in SPNEGO */
71 msg1
= spnego_gen_auth(blob_out
);
74 data_blob_free(&blob_out
);
76 cred
.bv_val
= (char *)msg1
.data
;
77 cred
.bv_len
= msg1
.length
;
79 rc
= ldap_sasl_bind_s(ads
->ld
, NULL
, "GSS-SPNEGO", &cred
, NULL
, NULL
, &scred
);
80 data_blob_free(&msg1
);
81 if ((rc
!= LDAP_SASL_BIND_IN_PROGRESS
) && (rc
!= 0)) {
86 ntlmssp_end(&ntlmssp_state
);
90 blob
= data_blob(scred
->bv_val
, scred
->bv_len
);
93 blob
= data_blob(NULL
, 0);
98 ntlmssp_end(&ntlmssp_state
);
99 data_blob_free(&blob_out
);
100 return ADS_ERROR_NT(nt_status
);
104 (rc
== LDAP_SASL_BIND_IN_PROGRESS
)) {
105 DATA_BLOB tmp_blob
= data_blob(NULL
, 0);
106 /* the server might give us back two challenges */
107 if (!spnego_parse_challenge(blob
, &blob_in
,
110 ntlmssp_end(&ntlmssp_state
);
111 data_blob_free(&blob
);
112 DEBUG(3,("Failed to parse challenges\n"));
113 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
115 data_blob_free(&tmp_blob
);
116 } else if (rc
== LDAP_SASL_BIND_IN_PROGRESS
) {
117 if (!spnego_parse_auth_response(blob
, nt_status
,
120 ntlmssp_end(&ntlmssp_state
);
121 data_blob_free(&blob
);
122 DEBUG(3,("Failed to parse auth response\n"));
123 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
126 data_blob_free(&blob
);
127 data_blob_free(&blob_out
);
129 } while (rc
== LDAP_SASL_BIND_IN_PROGRESS
&& !NT_STATUS_IS_OK(nt_status
));
131 /* we have a reference conter on ntlmssp_state, if we are signing
132 then the state will be kept by the signing engine */
134 ntlmssp_end(&ntlmssp_state
);
136 return ADS_ERROR(rc
);
140 perform a LDAP/SASL/SPNEGO/KRB5 bind
142 static ADS_STATUS
ads_sasl_spnego_krb5_bind(ADS_STRUCT
*ads
, const char *principal
)
144 DATA_BLOB blob
= data_blob(NULL
, 0);
145 struct berval cred
, *scred
= NULL
;
146 DATA_BLOB session_key
= data_blob(NULL
, 0);
149 rc
= spnego_gen_negTokenTarg(principal
, ads
->auth
.time_offset
, &blob
, &session_key
, 0);
152 return ADS_ERROR_KRB5(rc
);
155 /* now send the auth packet and we should be done */
156 cred
.bv_val
= (char *)blob
.data
;
157 cred
.bv_len
= blob
.length
;
159 rc
= ldap_sasl_bind_s(ads
->ld
, NULL
, "GSS-SPNEGO", &cred
, NULL
, NULL
, &scred
);
161 data_blob_free(&blob
);
162 data_blob_free(&session_key
);
166 return ADS_ERROR(rc
);
170 this performs a SASL/SPNEGO bind
172 static ADS_STATUS
ads_sasl_spnego_bind(ADS_STRUCT
*ads
)
174 struct berval
*scred
=NULL
;
178 char *principal
= NULL
;
179 char *OIDs
[ASN1_MAX_OIDS
];
181 BOOL got_kerberos_mechanism
= False
;
184 rc
= ldap_sasl_bind_s(ads
->ld
, NULL
, "GSS-SPNEGO", NULL
, NULL
, NULL
, &scred
);
186 if (rc
!= LDAP_SASL_BIND_IN_PROGRESS
) {
187 status
= ADS_ERROR(rc
);
191 blob
= data_blob(scred
->bv_val
, scred
->bv_len
);
196 file_save("sasl_spnego.dat", blob
.data
, blob
.length
);
199 /* the server sent us the first part of the SPNEGO exchange in the negprot
201 if (!spnego_parse_negTokenInit(blob
, OIDs
, &principal
)) {
202 data_blob_free(&blob
);
203 status
= ADS_ERROR(LDAP_OPERATIONS_ERROR
);
206 data_blob_free(&blob
);
208 /* make sure the server understands kerberos */
209 for (i
=0;OIDs
[i
];i
++) {
210 DEBUG(3,("ads_sasl_spnego_bind: got OID=%s\n", OIDs
[i
]));
212 if (strcmp(OIDs
[i
], OID_KERBEROS5_OLD
) == 0 ||
213 strcmp(OIDs
[i
], OID_KERBEROS5
) == 0) {
214 got_kerberos_mechanism
= True
;
219 DEBUG(3,("ads_sasl_spnego_bind: got server principal name =%s\n", principal
));
222 if (!(ads
->auth
.flags
& ADS_AUTH_DISABLE_KERBEROS
) &&
223 got_kerberos_mechanism
) {
224 status
= ads_sasl_spnego_krb5_bind(ads
, principal
);
225 if (ADS_ERR_OK(status
)) {
226 SAFE_FREE(principal
);
230 status
= ADS_ERROR_KRB5(ads_kinit_password(ads
));
232 if (ADS_ERR_OK(status
)) {
233 status
= ads_sasl_spnego_krb5_bind(ads
, principal
);
236 /* only fallback to NTLMSSP if allowed */
237 if (ADS_ERR_OK(status
) ||
238 !(ads
->auth
.flags
& ADS_AUTH_ALLOW_NTLMSSP
)) {
239 SAFE_FREE(principal
);
245 SAFE_FREE(principal
);
247 /* lets do NTLMSSP ... this has the big advantage that we don't need
248 to sync clocks, and we don't rely on special versions of the krb5
249 library for HMAC_MD4 encryption */
250 return ads_sasl_spnego_ntlmssp_bind(ads
);
257 #define MAX_GSS_PASSES 3
259 /* this performs a SASL/gssapi bind
260 we avoid using cyrus-sasl to make Samba more robust. cyrus-sasl
261 is very dependent on correctly configured DNS whereas
262 this routine is much less fragile
263 see RFC2078 and RFC2222 for details
265 static ADS_STATUS
ads_sasl_gssapi_bind(ADS_STRUCT
*ads
)
268 gss_name_t serv_name
;
269 gss_buffer_desc input_name
;
270 gss_ctx_id_t context_handle
;
271 gss_OID mech_type
= GSS_C_NULL_OID
;
272 gss_buffer_desc output_token
, input_token
;
273 uint32 ret_flags
, conf_state
;
275 struct berval
*scred
= NULL
;
283 krb5_principal principal
;
284 krb5_context ctx
= NULL
;
285 krb5_enctype enc_types
[] = {
286 #ifdef ENCTYPE_ARCFOUR_HMAC
287 ENCTYPE_ARCFOUR_HMAC
,
291 gss_OID_desc nt_principal
=
292 {10, CONST_DISCARD(char *, "\052\206\110\206\367\022\001\002\002\002")};
294 /* we need to fetch a service ticket as the ldap user in the
295 servers realm, regardless of our realm */
296 asprintf(&sname
, "ldap/%s@%s", ads
->config
.ldap_server_name
, ads
->config
.realm
);
297 krb5_init_context(&ctx
);
298 krb5_set_default_tgs_ktypes(ctx
, enc_types
);
299 krb5_parse_name(ctx
, sname
, &principal
);
301 krb5_free_context(ctx
);
303 input_name
.value
= &principal
;
304 input_name
.length
= sizeof(principal
);
306 gss_rc
= gss_import_name(&minor_status
,&input_name
,&nt_principal
, &serv_name
);
308 return ADS_ERROR_GSS(gss_rc
, minor_status
);
311 context_handle
= GSS_C_NO_CONTEXT
;
313 input_token
.value
= NULL
;
314 input_token
.length
= 0;
316 for (i
=0; i
< MAX_GSS_PASSES
; i
++) {
317 gss_rc
= gss_init_sec_context(&minor_status
,
322 GSS_C_MUTUAL_FLAG
| GSS_C_REPLAY_FLAG
,
331 if (input_token
.value
) {
332 gss_release_buffer(&minor_status
, &input_token
);
335 if (gss_rc
&& gss_rc
!= GSS_S_CONTINUE_NEEDED
) {
336 status
= ADS_ERROR_GSS(gss_rc
, minor_status
);
340 cred
.bv_val
= output_token
.value
;
341 cred
.bv_len
= output_token
.length
;
343 rc
= ldap_sasl_bind_s(ads
->ld
, NULL
, "GSSAPI", &cred
, NULL
, NULL
,
345 if (rc
!= LDAP_SASL_BIND_IN_PROGRESS
) {
346 status
= ADS_ERROR(rc
);
350 if (output_token
.value
) {
351 gss_release_buffer(&minor_status
, &output_token
);
355 input_token
.value
= scred
->bv_val
;
356 input_token
.length
= scred
->bv_len
;
358 input_token
.value
= NULL
;
359 input_token
.length
= 0;
362 if (gss_rc
== 0) break;
365 gss_release_name(&minor_status
, &serv_name
);
367 gss_rc
= gss_unwrap(&minor_status
,context_handle
,&input_token
,&output_token
,
368 (int *)&conf_state
,NULL
);
370 status
= ADS_ERROR_GSS(gss_rc
, minor_status
);
374 gss_release_buffer(&minor_status
, &input_token
);
376 p
= (uint8
*)output_token
.value
;
378 file_save("sasl_gssapi.dat", output_token
.value
, output_token
.length
);
380 max_msg_size
= (p
[1]<<16) | (p
[2]<<8) | p
[3];
383 gss_release_buffer(&minor_status
, &output_token
);
385 output_token
.value
= SMB_MALLOC(strlen(ads
->config
.bind_path
) + 8);
386 p
= output_token
.value
;
388 *p
++ = 1; /* no sign & seal selection */
389 /* choose the same size as the server gave us */
390 *p
++ = max_msg_size
>>16;
391 *p
++ = max_msg_size
>>8;
393 snprintf((char *)p
, strlen(ads
->config
.bind_path
)+4, "dn:%s", ads
->config
.bind_path
);
394 p
+= strlen((const char *)p
);
396 output_token
.length
= PTR_DIFF(p
, output_token
.value
);
398 gss_rc
= gss_wrap(&minor_status
, context_handle
,0,GSS_C_QOP_DEFAULT
,
399 &output_token
, (int *)&conf_state
,
402 status
= ADS_ERROR_GSS(gss_rc
, minor_status
);
406 free(output_token
.value
);
408 cred
.bv_val
= input_token
.value
;
409 cred
.bv_len
= input_token
.length
;
411 rc
= ldap_sasl_bind_s(ads
->ld
, NULL
, "GSSAPI", &cred
, NULL
, NULL
,
413 status
= ADS_ERROR(rc
);
415 gss_release_buffer(&minor_status
, &input_token
);
424 /* mapping between SASL mechanisms and functions */
427 ADS_STATUS (*fn
)(ADS_STRUCT
*);
428 } sasl_mechanisms
[] = {
429 {"GSS-SPNEGO", ads_sasl_spnego_bind
},
431 {"GSSAPI", ads_sasl_gssapi_bind
}, /* doesn't work with .NET RC1. No idea why */
436 ADS_STATUS
ads_sasl_bind(ADS_STRUCT
*ads
)
438 const char *attrs
[] = {"supportedSASLMechanisms", NULL
};
444 /* get a list of supported SASL mechanisms */
445 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
446 if (!ADS_ERR_OK(status
)) return status
;
448 values
= ldap_get_values(ads
->ld
, res
, "supportedSASLMechanisms");
450 /* try our supported mechanisms in order */
451 for (i
=0;sasl_mechanisms
[i
].name
;i
++) {
452 /* see if the server supports it */
453 for (j
=0;values
&& values
[j
];j
++) {
454 if (strcmp(values
[j
], sasl_mechanisms
[i
].name
) == 0) {
455 DEBUG(4,("Found SASL mechanism %s\n", values
[j
]));
456 status
= sasl_mechanisms
[i
].fn(ads
);
457 ldap_value_free(values
);
464 ldap_value_free(values
);
466 return ADS_ERROR(LDAP_AUTH_METHOD_NOT_SUPPORTED
);