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);
46 if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_username(ntlmssp_state, ads->auth.user_name))) {
47 return ADS_ERROR_NT(nt_status);
49 if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_domain(ntlmssp_state, ads->auth.realm))) {
50 return ADS_ERROR_NT(nt_status);
52 if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_password(ntlmssp_state, ads->auth.password))) {
53 return ADS_ERROR_NT(nt_status);
56 blob_in = data_blob(NULL, 0);
59 nt_status = ntlmssp_update(ntlmssp_state,
61 data_blob_free(&blob_in);
62 if ((NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)
63 || NT_STATUS_IS_OK(nt_status))
66 /* and wrap it in a SPNEGO wrapper */
67 msg1 = gen_negTokenInit(OID_NTLMSSP, blob_out);
69 /* wrap it in SPNEGO */
70 msg1 = spnego_gen_auth(blob_out);
73 data_blob_free(&blob_out);
75 cred.bv_val = (char *)msg1.data;
76 cred.bv_len = msg1.length;
78 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
79 data_blob_free(&msg1);
80 if ((rc != LDAP_SASL_BIND_IN_PROGRESS) && (rc != 0)) {
85 ntlmssp_end(&ntlmssp_state);
89 blob = data_blob(scred->bv_val, scred->bv_len);
92 blob = data_blob(NULL, 0);
97 ntlmssp_end(&ntlmssp_state);
98 data_blob_free(&blob_out);
99 return ADS_ERROR_NT(nt_status);
103 (rc == LDAP_SASL_BIND_IN_PROGRESS)) {
104 DATA_BLOB tmp_blob = data_blob(NULL, 0);
105 /* the server might give us back two challenges */
106 if (!spnego_parse_challenge(blob, &blob_in,
109 ntlmssp_end(&ntlmssp_state);
110 data_blob_free(&blob);
111 DEBUG(3,("Failed to parse challenges\n"));
112 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
114 data_blob_free(&tmp_blob);
115 } else if (rc == LDAP_SASL_BIND_IN_PROGRESS) {
116 if (!spnego_parse_auth_response(blob, nt_status,
119 ntlmssp_end(&ntlmssp_state);
120 data_blob_free(&blob);
121 DEBUG(3,("Failed to parse auth response\n"));
122 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
125 data_blob_free(&blob);
126 data_blob_free(&blob_out);
128 } while (rc == LDAP_SASL_BIND_IN_PROGRESS && !NT_STATUS_IS_OK(nt_status));
130 /* we have a reference conter on ntlmssp_state, if we are signing
131 then the state will be kept by the signing engine */
133 ntlmssp_end(&ntlmssp_state);
135 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);
171 this performs a SASL/SPNEGO bind
173 static ADS_STATUS ads_sasl_spnego_bind(ADS_STRUCT *ads)
175 struct berval *scred=NULL;
179 char *principal = NULL;
180 char *OIDs[ASN1_MAX_OIDS];
182 BOOL got_kerberos_mechanism = False;
185 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", NULL, NULL, NULL, &scred);
187 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
188 status = ADS_ERROR(rc);
192 blob = data_blob(scred->bv_val, scred->bv_len);
197 file_save("sasl_spnego.dat", blob.data, blob.length);
200 /* the server sent us the first part of the SPNEGO exchange in the negprot
202 if (!spnego_parse_negTokenInit(blob, OIDs, &principal)) {
203 data_blob_free(&blob);
204 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
207 data_blob_free(&blob);
209 /* make sure the server understands kerberos */
210 for (i=0;OIDs[i];i++) {
211 DEBUG(3,("ads_sasl_spnego_bind: got OID=%s\n", OIDs[i]));
213 if (strcmp(OIDs[i], OID_KERBEROS5_OLD) == 0 ||
214 strcmp(OIDs[i], OID_KERBEROS5) == 0) {
215 got_kerberos_mechanism = True;
220 DEBUG(3,("ads_sasl_spnego_bind: got server principal name =%s\n", principal));
223 if (!(ads->auth.flags & ADS_AUTH_DISABLE_KERBEROS) &&
224 got_kerberos_mechanism) {
225 status = ads_sasl_spnego_krb5_bind(ads, principal);
226 if (ADS_ERR_OK(status)) {
227 SAFE_FREE(principal);
231 status = ADS_ERROR_KRB5(ads_kinit_password(ads));
233 if (ADS_ERR_OK(status)) {
234 status = ads_sasl_spnego_krb5_bind(ads, principal);
237 /* only fallback to NTLMSSP if allowed */
238 if (ADS_ERR_OK(status) ||
239 !(ads->auth.flags & ADS_AUTH_ALLOW_NTLMSSP)) {
240 SAFE_FREE(principal);
246 SAFE_FREE(principal);
248 /* lets do NTLMSSP ... this has the big advantage that we don't need
249 to sync clocks, and we don't rely on special versions of the krb5
250 library for HMAC_MD4 encryption */
251 return ads_sasl_spnego_ntlmssp_bind(ads);
258 #define MAX_GSS_PASSES 3
260 /* this performs a SASL/gssapi bind
261 we avoid using cyrus-sasl to make Samba more robust. cyrus-sasl
262 is very dependent on correctly configured DNS whereas
263 this routine is much less fragile
264 see RFC2078 and RFC2222 for details
266 static ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads)
269 gss_name_t serv_name;
270 gss_buffer_desc input_name;
271 gss_ctx_id_t context_handle;
272 gss_OID mech_type = GSS_C_NULL_OID;
273 gss_buffer_desc output_token, input_token;
274 uint32 ret_flags, conf_state;
276 struct berval *scred = NULL;
280 uint32 max_msg_size = 0;
283 krb5_principal principal;
284 krb5_context ctx = NULL;
285 gss_OID_desc nt_principal =
286 {10, CONST_DISCARD(char *, "\052\206\110\206\367\022\001\002\002\002")};
288 /* we need to fetch a service ticket as the ldap user in the
289 servers realm, regardless of our realm */
290 asprintf(&sname, "ldap/%s@%s", ads->config.ldap_server_name, ads->config.realm);
292 initialize_krb5_error_table();
293 status = ADS_ERROR_KRB5(krb5_init_context(&ctx));
294 if (!ADS_ERR_OK(status)) {
297 status = ADS_ERROR_KRB5(smb_krb5_parse_name(ctx, sname, &principal));
298 if (!ADS_ERR_OK(status)) {
303 krb5_free_context(ctx);
305 input_name.value = &principal;
306 input_name.length = sizeof(principal);
308 gss_rc = gss_import_name(&minor_status, &input_name, &nt_principal, &serv_name);
310 return ADS_ERROR_GSS(gss_rc, minor_status);
313 context_handle = GSS_C_NO_CONTEXT;
315 input_token.value = NULL;
316 input_token.length = 0;
318 for (i=0; i < MAX_GSS_PASSES; i++) {
319 gss_rc = gss_init_sec_context(&minor_status,
324 GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG,
333 if (input_token.value) {
334 gss_release_buffer(&minor_status, &input_token);
337 if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) {
338 status = ADS_ERROR_GSS(gss_rc, minor_status);
342 cred.bv_val = (char *)output_token.value;
343 cred.bv_len = output_token.length;
345 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL,
347 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
348 status = ADS_ERROR(rc);
352 if (output_token.value) {
353 gss_release_buffer(&minor_status, &output_token);
357 input_token.value = scred->bv_val;
358 input_token.length = scred->bv_len;
360 input_token.value = NULL;
361 input_token.length = 0;
364 if (gss_rc == 0) break;
367 gss_release_name(&minor_status, &serv_name);
369 gss_rc = gss_unwrap(&minor_status,context_handle,&input_token,&output_token,
370 (int *)&conf_state,NULL);
372 status = ADS_ERROR_GSS(gss_rc, minor_status);
376 gss_release_buffer(&minor_status, &input_token);
378 p = (uint8 *)output_token.value;
381 file_save("sasl_gssapi.dat", output_token.value, output_token.length);
385 max_msg_size = (p[1]<<16) | (p[2]<<8) | p[3];
388 gss_release_buffer(&minor_status, &output_token);
390 output_token.value = SMB_MALLOC(strlen(ads->config.bind_path) + 8);
391 p = (uint8 *)output_token.value;
393 *p++ = 1; /* no sign & seal selection */
394 /* choose the same size as the server gave us */
395 *p++ = max_msg_size>>16;
396 *p++ = max_msg_size>>8;
398 snprintf((char *)p, strlen(ads->config.bind_path)+4, "dn:%s", ads->config.bind_path);
399 p += strlen((const char *)p);
401 output_token.length = PTR_DIFF(p, output_token.value);
403 gss_rc = gss_wrap(&minor_status, context_handle,0,GSS_C_QOP_DEFAULT,
404 &output_token, (int *)&conf_state,
407 status = ADS_ERROR_GSS(gss_rc, minor_status);
411 free(output_token.value);
413 cred.bv_val = (char *)input_token.value;
414 cred.bv_len = input_token.length;
416 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL,
418 status = ADS_ERROR(rc);
420 gss_release_buffer(&minor_status, &input_token);
427 #endif /* HAVE_GGSAPI */
429 /* mapping between SASL mechanisms and functions */
432 ADS_STATUS (*fn)(ADS_STRUCT *);
433 } sasl_mechanisms[] = {
434 {"GSS-SPNEGO", ads_sasl_spnego_bind},
436 {"GSSAPI", ads_sasl_gssapi_bind}, /* doesn't work with .NET RC1. No idea why */
441 ADS_STATUS ads_sasl_bind(ADS_STRUCT *ads)
443 const char *attrs[] = {"supportedSASLMechanisms", NULL};
449 /* get a list of supported SASL mechanisms */
450 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
451 if (!ADS_ERR_OK(status)) return status;
453 values = ldap_get_values(ads->ld, res, "supportedSASLMechanisms");
455 /* try our supported mechanisms in order */
456 for (i=0;sasl_mechanisms[i].name;i++) {
457 /* see if the server supports it */
458 for (j=0;values && values[j];j++) {
459 if (strcmp(values[j], sasl_mechanisms[i].name) == 0) {
460 DEBUG(4,("Found SASL mechanism %s\n", values[j]));
461 status = sasl_mechanisms[i].fn(ads);
462 ldap_value_free(values);
469 ldap_value_free(values);
471 return ADS_ERROR(LDAP_AUTH_METHOD_NOT_SUPPORTED);
474 #endif /* HAVE_LDAP */