r17915: Saturn fixes
[Samba.git] / source / libads / sasl.c
blob52a0f0b165cf430d87d0d7371060f117b90aecd0
1 /*
2 Unix SMB/CIFS implementation.
3 ads sasl code
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.
21 #include "includes.h"
23 #ifdef HAVE_LDAP
25 /*
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;
36 int rc;
37 NTSTATUS nt_status;
38 int turn = 1;
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);
59 do {
60 nt_status = ntlmssp_update(ntlmssp_state,
61 blob_in, &blob_out);
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))
65 && blob_out.length) {
66 if (turn == 1) {
67 /* and wrap it in a SPNEGO wrapper */
68 msg1 = gen_negTokenInit(OID_NTLMSSP, blob_out);
69 } else {
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;
78 scred = NULL;
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)) {
82 if (scred) {
83 ber_bvfree(scred);
86 ntlmssp_end(&ntlmssp_state);
87 return ADS_ERROR(rc);
89 if (scred) {
90 blob = data_blob(scred->bv_val, scred->bv_len);
91 ber_bvfree(scred);
92 } else {
93 blob = data_blob(NULL, 0);
96 } else {
98 ntlmssp_end(&ntlmssp_state);
99 data_blob_free(&blob_out);
100 return ADS_ERROR_NT(nt_status);
103 if ((turn == 1) &&
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,
108 &tmp_blob)) {
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,
118 &blob_in)) {
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);
128 turn++;
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);
147 int rc;
149 rc = spnego_gen_negTokenTarg(principal, ads->auth.time_offset, &blob, &session_key, 0);
151 if (rc) {
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);
163 if(scred)
164 ber_bvfree(scred);
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;
175 int rc, i;
176 ADS_STATUS status;
177 DATA_BLOB blob;
178 char *principal = NULL;
179 char *OIDs[ASN1_MAX_OIDS];
180 #ifdef HAVE_KRB5
181 BOOL got_kerberos_mechanism = False;
182 #endif
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);
188 goto failed;
191 blob = data_blob(scred->bv_val, scred->bv_len);
193 ber_bvfree(scred);
195 #if 0
196 file_save("sasl_spnego.dat", blob.data, blob.length);
197 #endif
199 /* the server sent us the first part of the SPNEGO exchange in the negprot
200 reply */
201 if (!spnego_parse_negTokenInit(blob, OIDs, &principal)) {
202 data_blob_free(&blob);
203 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
204 goto failed;
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]));
211 #ifdef HAVE_KRB5
212 if (strcmp(OIDs[i], OID_KERBEROS5_OLD) == 0 ||
213 strcmp(OIDs[i], OID_KERBEROS5) == 0) {
214 got_kerberos_mechanism = True;
216 #endif
217 free(OIDs[i]);
219 DEBUG(3,("ads_sasl_spnego_bind: got server principal name =%s\n", principal));
221 #ifdef HAVE_KRB5
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);
227 return status;
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);
240 return status;
243 #endif
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);
252 failed:
253 return status;
256 #ifdef HAVE_GSSAPI
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)
267 uint32 minor_status;
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;
274 struct berval cred;
275 struct berval *scred = NULL;
276 int i=0;
277 int gss_rc, rc;
278 uint8 *p;
279 uint32 max_msg_size = 0;
280 char *sname;
281 ADS_STATUS status;
282 krb5_principal principal;
283 krb5_context ctx = NULL;
284 krb5_enctype enc_types[] = {
285 #ifdef ENCTYPE_ARCFOUR_HMAC
286 ENCTYPE_ARCFOUR_HMAC,
287 #endif
288 ENCTYPE_DES_CBC_MD5,
289 ENCTYPE_NULL};
290 gss_OID_desc nt_principal =
291 {10, CONST_DISCARD(char *, "\052\206\110\206\367\022\001\002\002\002")};
293 /* we need to fetch a service ticket as the ldap user in the
294 servers realm, regardless of our realm */
295 asprintf(&sname, "ldap/%s@%s", ads->config.ldap_server_name, ads->config.realm);
297 initialize_krb5_error_table();
298 status = ADS_ERROR_KRB5(krb5_init_context(&ctx));
299 if (!ADS_ERR_OK(status)) {
300 return status;
302 status = ADS_ERROR_KRB5(krb5_set_default_tgs_ktypes(ctx, enc_types));
303 if (!ADS_ERR_OK(status)) {
304 return status;
306 status = ADS_ERROR_KRB5(smb_krb5_parse_name(ctx, sname, &principal));
307 if (!ADS_ERR_OK(status)) {
308 return status;
311 free(sname);
312 krb5_free_context(ctx);
314 input_name.value = &principal;
315 input_name.length = sizeof(principal);
317 gss_rc = gss_import_name(&minor_status, &input_name, &nt_principal, &serv_name);
318 if (gss_rc) {
319 return ADS_ERROR_GSS(gss_rc, minor_status);
322 context_handle = GSS_C_NO_CONTEXT;
324 input_token.value = NULL;
325 input_token.length = 0;
327 for (i=0; i < MAX_GSS_PASSES; i++) {
328 gss_rc = gss_init_sec_context(&minor_status,
329 GSS_C_NO_CREDENTIAL,
330 &context_handle,
331 serv_name,
332 mech_type,
333 GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG,
335 NULL,
336 &input_token,
337 NULL,
338 &output_token,
339 &ret_flags,
340 NULL);
342 if (input_token.value) {
343 gss_release_buffer(&minor_status, &input_token);
346 if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) {
347 status = ADS_ERROR_GSS(gss_rc, minor_status);
348 goto failed;
351 cred.bv_val = output_token.value;
352 cred.bv_len = output_token.length;
354 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL,
355 &scred);
356 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
357 status = ADS_ERROR(rc);
358 goto failed;
361 if (output_token.value) {
362 gss_release_buffer(&minor_status, &output_token);
365 if (scred) {
366 input_token.value = scred->bv_val;
367 input_token.length = scred->bv_len;
368 } else {
369 input_token.value = NULL;
370 input_token.length = 0;
373 if (gss_rc == 0) break;
376 gss_release_name(&minor_status, &serv_name);
378 gss_rc = gss_unwrap(&minor_status,context_handle,&input_token,&output_token,
379 (int *)&conf_state,NULL);
380 if (gss_rc) {
381 status = ADS_ERROR_GSS(gss_rc, minor_status);
382 goto failed;
385 gss_release_buffer(&minor_status, &input_token);
387 p = (uint8 *)output_token.value;
389 #if 0
390 file_save("sasl_gssapi.dat", output_token.value, output_token.length);
391 #endif
392 if (p) {
393 max_msg_size = (p[1]<<16) | (p[2]<<8) | p[3];
396 gss_release_buffer(&minor_status, &output_token);
398 output_token.value = SMB_MALLOC(strlen(ads->config.bind_path) + 8);
399 p = output_token.value;
401 *p++ = 1; /* no sign & seal selection */
402 /* choose the same size as the server gave us */
403 *p++ = max_msg_size>>16;
404 *p++ = max_msg_size>>8;
405 *p++ = max_msg_size;
406 snprintf((char *)p, strlen(ads->config.bind_path)+4, "dn:%s", ads->config.bind_path);
407 p += strlen((const char *)p);
409 output_token.length = PTR_DIFF(p, output_token.value);
411 gss_rc = gss_wrap(&minor_status, context_handle,0,GSS_C_QOP_DEFAULT,
412 &output_token, (int *)&conf_state,
413 &input_token);
414 if (gss_rc) {
415 status = ADS_ERROR_GSS(gss_rc, minor_status);
416 goto failed;
419 free(output_token.value);
421 cred.bv_val = input_token.value;
422 cred.bv_len = input_token.length;
424 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL,
425 &scred);
426 status = ADS_ERROR(rc);
428 gss_release_buffer(&minor_status, &input_token);
430 failed:
431 if(scred)
432 ber_bvfree(scred);
433 return status;
435 #endif /* HAVE_GGSAPI */
437 /* mapping between SASL mechanisms and functions */
438 static struct {
439 const char *name;
440 ADS_STATUS (*fn)(ADS_STRUCT *);
441 } sasl_mechanisms[] = {
442 {"GSS-SPNEGO", ads_sasl_spnego_bind},
443 #ifdef HAVE_GSSAPI
444 {"GSSAPI", ads_sasl_gssapi_bind}, /* doesn't work with .NET RC1. No idea why */
445 #endif
446 {NULL, NULL}
449 ADS_STATUS ads_sasl_bind(ADS_STRUCT *ads)
451 const char *attrs[] = {"supportedSASLMechanisms", NULL};
452 char **values;
453 ADS_STATUS status;
454 int i, j;
455 void *res;
457 /* get a list of supported SASL mechanisms */
458 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
459 if (!ADS_ERR_OK(status)) return status;
461 values = ldap_get_values(ads->ld, res, "supportedSASLMechanisms");
463 /* try our supported mechanisms in order */
464 for (i=0;sasl_mechanisms[i].name;i++) {
465 /* see if the server supports it */
466 for (j=0;values && values[j];j++) {
467 if (strcmp(values[j], sasl_mechanisms[i].name) == 0) {
468 DEBUG(4,("Found SASL mechanism %s\n", values[j]));
469 status = sasl_mechanisms[i].fn(ads);
470 ldap_value_free(values);
471 ldap_msgfree(res);
472 return status;
477 ldap_value_free(values);
478 ldap_msgfree(res);
479 return ADS_ERROR(LDAP_AUTH_METHOD_NOT_SUPPORTED);
482 #endif /* HAVE_LDAP */