s4/test-libnet: Helper func to initialize libnet_context
[Samba.git] / source3 / libads / sasl.c
blob04b9a71d76e2498be12fd4636631fed448469ce5
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 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/>.
20 #include "includes.h"
21 #include "../libcli/auth/spnego.h"
22 #include "../libcli/auth/ntlmssp.h"
24 #ifdef HAVE_LDAP
26 static ADS_STATUS ads_sasl_ntlmssp_wrap(ADS_STRUCT *ads, uint8 *buf, uint32 len)
28 struct ntlmssp_state *ntlmssp_state =
29 (struct ntlmssp_state *)ads->ldap.wrap_private_data;
30 ADS_STATUS status;
31 NTSTATUS nt_status;
32 DATA_BLOB sig;
33 TALLOC_CTX *frame;
34 uint8 *dptr = ads->ldap.out.buf + (4 + NTLMSSP_SIG_SIZE);
36 frame = talloc_stackframe();
37 /* copy the data to the right location */
38 memcpy(dptr, buf, len);
40 /* create the signature and may encrypt the data */
41 if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_SEAL) {
42 nt_status = ntlmssp_seal_packet(ntlmssp_state,
43 frame,
44 dptr, len,
45 dptr, len,
46 &sig);
47 } else {
48 nt_status = ntlmssp_sign_packet(ntlmssp_state,
49 frame,
50 dptr, len,
51 dptr, len,
52 &sig);
54 status = ADS_ERROR_NT(nt_status);
55 if (!ADS_ERR_OK(status)) return status;
57 /* copy the signature to the right location */
58 memcpy(ads->ldap.out.buf + 4,
59 sig.data, NTLMSSP_SIG_SIZE);
61 TALLOC_FREE(frame);
63 /* set how many bytes must be written to the underlying socket */
64 ads->ldap.out.left = 4 + NTLMSSP_SIG_SIZE + len;
66 return ADS_SUCCESS;
69 static ADS_STATUS ads_sasl_ntlmssp_unwrap(ADS_STRUCT *ads)
71 struct ntlmssp_state *ntlmssp_state =
72 (struct ntlmssp_state *)ads->ldap.wrap_private_data;
73 ADS_STATUS status;
74 NTSTATUS nt_status;
75 DATA_BLOB sig;
76 uint8 *dptr = ads->ldap.in.buf + (4 + NTLMSSP_SIG_SIZE);
77 uint32 dlen = ads->ldap.in.ofs - (4 + NTLMSSP_SIG_SIZE);
79 /* wrap the signature into a DATA_BLOB */
80 sig = data_blob_const(ads->ldap.in.buf + 4, NTLMSSP_SIG_SIZE);
82 /* verify the signature and maybe decrypt the data */
83 if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_SEAL) {
84 nt_status = ntlmssp_unseal_packet(ntlmssp_state,
85 dptr, dlen,
86 dptr, dlen,
87 &sig);
88 } else {
89 nt_status = ntlmssp_check_packet(ntlmssp_state,
90 dptr, dlen,
91 dptr, dlen,
92 &sig);
94 status = ADS_ERROR_NT(nt_status);
95 if (!ADS_ERR_OK(status)) return status;
97 /* set the amount of bytes for the upper layer and set the ofs to the data */
98 ads->ldap.in.left = dlen;
99 ads->ldap.in.ofs = 4 + NTLMSSP_SIG_SIZE;
101 return ADS_SUCCESS;
104 static void ads_sasl_ntlmssp_disconnect(ADS_STRUCT *ads)
106 struct ntlmssp_state *ntlmssp_state =
107 (struct ntlmssp_state *)ads->ldap.wrap_private_data;
109 ntlmssp_end(&ntlmssp_state);
111 ads->ldap.wrap_ops = NULL;
112 ads->ldap.wrap_private_data = NULL;
115 static const struct ads_saslwrap_ops ads_sasl_ntlmssp_ops = {
116 .name = "ntlmssp",
117 .wrap = ads_sasl_ntlmssp_wrap,
118 .unwrap = ads_sasl_ntlmssp_unwrap,
119 .disconnect = ads_sasl_ntlmssp_disconnect
123 perform a LDAP/SASL/SPNEGO/NTLMSSP bind (just how many layers can
124 we fit on one socket??)
126 static ADS_STATUS ads_sasl_spnego_ntlmssp_bind(ADS_STRUCT *ads)
128 DATA_BLOB msg1 = data_blob_null;
129 DATA_BLOB blob = data_blob_null;
130 DATA_BLOB blob_in = data_blob_null;
131 DATA_BLOB blob_out = data_blob_null;
132 struct berval cred, *scred = NULL;
133 int rc;
134 NTSTATUS nt_status;
135 ADS_STATUS status;
136 int turn = 1;
137 uint32 features = 0;
139 struct ntlmssp_state *ntlmssp_state;
141 nt_status = ntlmssp_client_start(NULL,
142 global_myname(),
143 lp_workgroup(),
144 lp_client_ntlmv2_auth(),
145 &ntlmssp_state);
146 if (!NT_STATUS_IS_OK(nt_status)) {
147 return ADS_ERROR_NT(nt_status);
149 ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_SIGN;
151 if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_username(ntlmssp_state, ads->auth.user_name))) {
152 return ADS_ERROR_NT(nt_status);
154 if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_domain(ntlmssp_state, ads->auth.realm))) {
155 return ADS_ERROR_NT(nt_status);
157 if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_password(ntlmssp_state, ads->auth.password))) {
158 return ADS_ERROR_NT(nt_status);
161 switch (ads->ldap.wrap_type) {
162 case ADS_SASLWRAP_TYPE_SEAL:
163 features = NTLMSSP_FEATURE_SIGN | NTLMSSP_FEATURE_SEAL;
164 break;
165 case ADS_SASLWRAP_TYPE_SIGN:
166 if (ads->auth.flags & ADS_AUTH_SASL_FORCE) {
167 features = NTLMSSP_FEATURE_SIGN;
168 } else {
170 * windows servers are broken with sign only,
171 * so we need to use seal here too
173 features = NTLMSSP_FEATURE_SIGN | NTLMSSP_FEATURE_SEAL;
174 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SEAL;
176 break;
177 case ADS_SASLWRAP_TYPE_PLAIN:
178 break;
181 ntlmssp_want_feature(ntlmssp_state, features);
183 blob_in = data_blob_null;
185 do {
186 nt_status = ntlmssp_update(ntlmssp_state,
187 blob_in, &blob_out);
188 data_blob_free(&blob_in);
189 if ((NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)
190 || NT_STATUS_IS_OK(nt_status))
191 && blob_out.length) {
192 if (turn == 1) {
193 /* and wrap it in a SPNEGO wrapper */
194 msg1 = gen_negTokenInit(OID_NTLMSSP, blob_out);
195 } else {
196 /* wrap it in SPNEGO */
197 msg1 = spnego_gen_auth(blob_out);
200 data_blob_free(&blob_out);
202 cred.bv_val = (char *)msg1.data;
203 cred.bv_len = msg1.length;
204 scred = NULL;
205 rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
206 data_blob_free(&msg1);
207 if ((rc != LDAP_SASL_BIND_IN_PROGRESS) && (rc != 0)) {
208 if (scred) {
209 ber_bvfree(scred);
212 ntlmssp_end(&ntlmssp_state);
213 return ADS_ERROR(rc);
215 if (scred) {
216 blob = data_blob(scred->bv_val, scred->bv_len);
217 ber_bvfree(scred);
218 } else {
219 blob = data_blob_null;
222 } else {
224 ntlmssp_end(&ntlmssp_state);
225 data_blob_free(&blob_out);
226 return ADS_ERROR_NT(nt_status);
229 if ((turn == 1) &&
230 (rc == LDAP_SASL_BIND_IN_PROGRESS)) {
231 DATA_BLOB tmp_blob = data_blob_null;
232 /* the server might give us back two challenges */
233 if (!spnego_parse_challenge(blob, &blob_in,
234 &tmp_blob)) {
236 ntlmssp_end(&ntlmssp_state);
237 data_blob_free(&blob);
238 DEBUG(3,("Failed to parse challenges\n"));
239 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
241 data_blob_free(&tmp_blob);
242 } else if (rc == LDAP_SASL_BIND_IN_PROGRESS) {
243 if (!spnego_parse_auth_response(blob, nt_status, OID_NTLMSSP,
244 &blob_in)) {
246 ntlmssp_end(&ntlmssp_state);
247 data_blob_free(&blob);
248 DEBUG(3,("Failed to parse auth response\n"));
249 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
252 data_blob_free(&blob);
253 data_blob_free(&blob_out);
254 turn++;
255 } while (rc == LDAP_SASL_BIND_IN_PROGRESS && !NT_STATUS_IS_OK(nt_status));
257 /* we have a reference conter on ntlmssp_state, if we are signing
258 then the state will be kept by the signing engine */
260 if (ads->ldap.wrap_type > ADS_SASLWRAP_TYPE_PLAIN) {
261 ads->ldap.out.max_unwrapped = ADS_SASL_WRAPPING_OUT_MAX_WRAPPED - NTLMSSP_SIG_SIZE;
262 ads->ldap.out.sig_size = NTLMSSP_SIG_SIZE;
263 ads->ldap.in.min_wrapped = ads->ldap.out.sig_size;
264 ads->ldap.in.max_wrapped = ADS_SASL_WRAPPING_IN_MAX_WRAPPED;
265 status = ads_setup_sasl_wrapping(ads, &ads_sasl_ntlmssp_ops, ntlmssp_state);
266 if (!ADS_ERR_OK(status)) {
267 DEBUG(0, ("ads_setup_sasl_wrapping() failed: %s\n",
268 ads_errstr(status)));
269 ntlmssp_end(&ntlmssp_state);
270 return status;
272 } else {
273 ntlmssp_end(&ntlmssp_state);
276 return ADS_ERROR(rc);
279 #ifdef HAVE_GSSAPI
280 static ADS_STATUS ads_sasl_gssapi_wrap(ADS_STRUCT *ads, uint8 *buf, uint32 len)
282 gss_ctx_id_t context_handle = (gss_ctx_id_t)ads->ldap.wrap_private_data;
283 ADS_STATUS status;
284 int gss_rc;
285 uint32 minor_status;
286 gss_buffer_desc unwrapped, wrapped;
287 int conf_req_flag, conf_state;
289 unwrapped.value = buf;
290 unwrapped.length = len;
292 /* for now request sign and seal */
293 conf_req_flag = (ads->ldap.wrap_type == ADS_SASLWRAP_TYPE_SEAL);
295 gss_rc = gss_wrap(&minor_status, context_handle,
296 conf_req_flag, GSS_C_QOP_DEFAULT,
297 &unwrapped, &conf_state,
298 &wrapped);
299 status = ADS_ERROR_GSS(gss_rc, minor_status);
300 if (!ADS_ERR_OK(status)) return status;
302 if (conf_req_flag && conf_state == 0) {
303 return ADS_ERROR_NT(NT_STATUS_ACCESS_DENIED);
306 if ((ads->ldap.out.size - 4) < wrapped.length) {
307 return ADS_ERROR_NT(NT_STATUS_INTERNAL_ERROR);
310 /* copy the wrapped blob to the right location */
311 memcpy(ads->ldap.out.buf + 4, wrapped.value, wrapped.length);
313 /* set how many bytes must be written to the underlying socket */
314 ads->ldap.out.left = 4 + wrapped.length;
316 gss_release_buffer(&minor_status, &wrapped);
318 return ADS_SUCCESS;
321 static ADS_STATUS ads_sasl_gssapi_unwrap(ADS_STRUCT *ads)
323 gss_ctx_id_t context_handle = (gss_ctx_id_t)ads->ldap.wrap_private_data;
324 ADS_STATUS status;
325 int gss_rc;
326 uint32 minor_status;
327 gss_buffer_desc unwrapped, wrapped;
328 int conf_state;
330 wrapped.value = ads->ldap.in.buf + 4;
331 wrapped.length = ads->ldap.in.ofs - 4;
333 gss_rc = gss_unwrap(&minor_status, context_handle,
334 &wrapped, &unwrapped,
335 &conf_state, GSS_C_QOP_DEFAULT);
336 status = ADS_ERROR_GSS(gss_rc, minor_status);
337 if (!ADS_ERR_OK(status)) return status;
339 if (ads->ldap.wrap_type == ADS_SASLWRAP_TYPE_SEAL && conf_state == 0) {
340 return ADS_ERROR_NT(NT_STATUS_ACCESS_DENIED);
343 if (wrapped.length < unwrapped.length) {
344 return ADS_ERROR_NT(NT_STATUS_INTERNAL_ERROR);
347 /* copy the wrapped blob to the right location */
348 memcpy(ads->ldap.in.buf + 4, unwrapped.value, unwrapped.length);
350 /* set how many bytes must be written to the underlying socket */
351 ads->ldap.in.left = unwrapped.length;
352 ads->ldap.in.ofs = 4;
354 gss_release_buffer(&minor_status, &unwrapped);
356 return ADS_SUCCESS;
359 static void ads_sasl_gssapi_disconnect(ADS_STRUCT *ads)
361 gss_ctx_id_t context_handle = (gss_ctx_id_t)ads->ldap.wrap_private_data;
362 uint32 minor_status;
364 gss_delete_sec_context(&minor_status, &context_handle, GSS_C_NO_BUFFER);
366 ads->ldap.wrap_ops = NULL;
367 ads->ldap.wrap_private_data = NULL;
370 static const struct ads_saslwrap_ops ads_sasl_gssapi_ops = {
371 .name = "gssapi",
372 .wrap = ads_sasl_gssapi_wrap,
373 .unwrap = ads_sasl_gssapi_unwrap,
374 .disconnect = ads_sasl_gssapi_disconnect
378 perform a LDAP/SASL/SPNEGO/GSSKRB5 bind
380 static ADS_STATUS ads_sasl_spnego_gsskrb5_bind(ADS_STRUCT *ads, const gss_name_t serv_name)
382 ADS_STATUS status;
383 bool ok;
384 uint32 minor_status;
385 int gss_rc, rc;
386 gss_OID_desc krb5_mech_type =
387 {9, CONST_DISCARD(char *, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02") };
388 gss_OID mech_type = &krb5_mech_type;
389 gss_OID actual_mech_type = GSS_C_NULL_OID;
390 const char *spnego_mechs[] = {OID_KERBEROS5_OLD, OID_KERBEROS5, OID_NTLMSSP, NULL};
391 gss_ctx_id_t context_handle = GSS_C_NO_CONTEXT;
392 gss_buffer_desc input_token, output_token;
393 uint32 req_flags, ret_flags;
394 uint32 req_tmp, ret_tmp;
395 DATA_BLOB unwrapped;
396 DATA_BLOB wrapped;
397 struct berval cred, *scred = NULL;
399 input_token.value = NULL;
400 input_token.length = 0;
402 req_flags = GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG;
403 switch (ads->ldap.wrap_type) {
404 case ADS_SASLWRAP_TYPE_SEAL:
405 req_flags |= GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG;
406 break;
407 case ADS_SASLWRAP_TYPE_SIGN:
408 req_flags |= GSS_C_INTEG_FLAG;
409 break;
410 case ADS_SASLWRAP_TYPE_PLAIN:
411 break;
414 /* Note: here we explicit ask for the krb5 mech_type */
415 gss_rc = gss_init_sec_context(&minor_status,
416 GSS_C_NO_CREDENTIAL,
417 &context_handle,
418 serv_name,
419 mech_type,
420 req_flags,
422 NULL,
423 &input_token,
424 &actual_mech_type,
425 &output_token,
426 &ret_flags,
427 NULL);
428 if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) {
429 status = ADS_ERROR_GSS(gss_rc, minor_status);
430 goto failed;
434 * As some gssapi krb5 mech implementations
435 * automaticly add GSS_C_INTEG_FLAG and GSS_C_CONF_FLAG
436 * to req_flags internaly, it's not possible to
437 * use plain or signing only connection via
438 * the gssapi interface.
440 * Because of this we need to check it the ret_flags
441 * has more flags as req_flags and correct the value
442 * of ads->ldap.wrap_type.
444 * I ads->auth.flags has ADS_AUTH_SASL_FORCE
445 * we need to give an error.
447 req_tmp = req_flags & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG);
448 ret_tmp = ret_flags & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG);
450 if (req_tmp == ret_tmp) {
451 /* everythings fine... */
453 } else if (req_flags & GSS_C_CONF_FLAG) {
455 * here we wanted sealing but didn't got it
456 * from the gssapi library
458 status = ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED);
459 goto failed;
461 } else if ((req_flags & GSS_C_INTEG_FLAG) &&
462 !(ret_flags & GSS_C_INTEG_FLAG)) {
464 * here we wanted siging but didn't got it
465 * from the gssapi library
467 status = ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED);
468 goto failed;
470 } else if (ret_flags & GSS_C_CONF_FLAG) {
472 * here we didn't want sealing
473 * but the gssapi library forces it
474 * so correct the needed wrap_type if
475 * the caller didn't forced siging only
477 if (ads->auth.flags & ADS_AUTH_SASL_FORCE) {
478 status = ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED);
479 goto failed;
482 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SEAL;
483 req_flags = ret_flags;
485 } else if (ret_flags & GSS_C_INTEG_FLAG) {
487 * here we didn't want signing
488 * but the gssapi library forces it
489 * so correct the needed wrap_type if
490 * the caller didn't forced plain
492 if (ads->auth.flags & ADS_AUTH_SASL_FORCE) {
493 status = ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED);
494 goto failed;
497 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SIGN;
498 req_flags = ret_flags;
499 } else {
501 * This could (should?) not happen
503 status = ADS_ERROR_NT(NT_STATUS_INTERNAL_ERROR);
504 goto failed;
508 /* and wrap that in a shiny SPNEGO wrapper */
509 unwrapped = data_blob_const(output_token.value, output_token.length);
510 wrapped = gen_negTokenTarg(spnego_mechs, unwrapped);
511 gss_release_buffer(&minor_status, &output_token);
512 if (unwrapped.length > wrapped.length) {
513 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
514 goto failed;
517 cred.bv_val = (char *)wrapped.data;
518 cred.bv_len = wrapped.length;
520 rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL,
521 &scred);
522 data_blob_free(&wrapped);
523 if (rc != LDAP_SUCCESS) {
524 status = ADS_ERROR(rc);
525 goto failed;
528 if (scred) {
529 wrapped = data_blob_const(scred->bv_val, scred->bv_len);
530 } else {
531 wrapped = data_blob_null;
534 ok = spnego_parse_auth_response(wrapped, NT_STATUS_OK,
535 OID_KERBEROS5_OLD,
536 &unwrapped);
537 if (scred) ber_bvfree(scred);
538 if (!ok) {
539 status = ADS_ERROR_NT(NT_STATUS_INVALID_NETWORK_RESPONSE);
540 goto failed;
543 input_token.value = unwrapped.data;
544 input_token.length = unwrapped.length;
547 * As we asked for mutal authentication
548 * we need to pass the servers response
549 * to gssapi
551 gss_rc = gss_init_sec_context(&minor_status,
552 GSS_C_NO_CREDENTIAL,
553 &context_handle,
554 serv_name,
555 mech_type,
556 req_flags,
558 NULL,
559 &input_token,
560 &actual_mech_type,
561 &output_token,
562 &ret_flags,
563 NULL);
564 data_blob_free(&unwrapped);
565 if (gss_rc) {
566 status = ADS_ERROR_GSS(gss_rc, minor_status);
567 goto failed;
570 gss_release_buffer(&minor_status, &output_token);
573 * If we the sign and seal options
574 * doesn't match after getting the response
575 * from the server, we don't want to use the connection
577 req_tmp = req_flags & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG);
578 ret_tmp = ret_flags & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG);
580 if (req_tmp != ret_tmp) {
581 /* everythings fine... */
582 status = ADS_ERROR_NT(NT_STATUS_INVALID_NETWORK_RESPONSE);
583 goto failed;
586 if (ads->ldap.wrap_type > ADS_SASLWRAP_TYPE_PLAIN) {
587 uint32 max_msg_size = ADS_SASL_WRAPPING_OUT_MAX_WRAPPED;
589 gss_rc = gss_wrap_size_limit(&minor_status, context_handle,
590 (ads->ldap.wrap_type == ADS_SASLWRAP_TYPE_SEAL),
591 GSS_C_QOP_DEFAULT,
592 max_msg_size, &ads->ldap.out.max_unwrapped);
593 if (gss_rc) {
594 status = ADS_ERROR_GSS(gss_rc, minor_status);
595 goto failed;
598 ads->ldap.out.sig_size = max_msg_size - ads->ldap.out.max_unwrapped;
599 ads->ldap.in.min_wrapped = 0x2C; /* taken from a capture with LDAP unbind */
600 ads->ldap.in.max_wrapped = max_msg_size;
601 status = ads_setup_sasl_wrapping(ads, &ads_sasl_gssapi_ops, context_handle);
602 if (!ADS_ERR_OK(status)) {
603 DEBUG(0, ("ads_setup_sasl_wrapping() failed: %s\n",
604 ads_errstr(status)));
605 goto failed;
607 /* make sure we don't free context_handle */
608 context_handle = GSS_C_NO_CONTEXT;
611 status = ADS_SUCCESS;
613 failed:
614 if (context_handle != GSS_C_NO_CONTEXT)
615 gss_delete_sec_context(&minor_status, &context_handle, GSS_C_NO_BUFFER);
616 return status;
619 #endif /* HAVE_GSSAPI */
621 #ifdef HAVE_KRB5
622 struct ads_service_principal {
623 char *string;
624 #ifdef HAVE_GSSAPI
625 gss_name_t name;
626 #endif
629 static void ads_free_service_principal(struct ads_service_principal *p)
631 SAFE_FREE(p->string);
633 #ifdef HAVE_GSSAPI
634 if (p->name) {
635 uint32 minor_status;
636 gss_release_name(&minor_status, &p->name);
638 #endif
639 ZERO_STRUCTP(p);
642 static ADS_STATUS ads_generate_service_principal(ADS_STRUCT *ads,
643 const char *given_principal,
644 struct ads_service_principal *p)
646 ADS_STATUS status;
647 #ifdef HAVE_GSSAPI
648 gss_buffer_desc input_name;
649 /* GSS_KRB5_NT_PRINCIPAL_NAME */
650 gss_OID_desc nt_principal =
651 {10, CONST_DISCARD(char *, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x01")};
652 uint32 minor_status;
653 int gss_rc;
654 #endif
656 ZERO_STRUCTP(p);
658 /* I've seen a child Windows 2000 domain not send
659 the principal name back in the first round of
660 the SASL bind reply. So we guess based on server
661 name and realm. --jerry */
662 /* Also try best guess when we get the w2k8 ignore
663 principal back - gd */
665 if (!given_principal ||
666 strequal(given_principal, ADS_IGNORE_PRINCIPAL)) {
668 status = ads_guess_service_principal(ads, &p->string);
669 if (!ADS_ERR_OK(status)) {
670 return status;
672 } else {
673 p->string = SMB_STRDUP(given_principal);
674 if (!p->string) {
675 return ADS_ERROR(LDAP_NO_MEMORY);
679 #ifdef HAVE_GSSAPI
680 input_name.value = p->string;
681 input_name.length = strlen(p->string);
683 gss_rc = gss_import_name(&minor_status, &input_name, &nt_principal, &p->name);
684 if (gss_rc) {
685 ads_free_service_principal(p);
686 return ADS_ERROR_GSS(gss_rc, minor_status);
688 #endif
690 return ADS_SUCCESS;
694 perform a LDAP/SASL/SPNEGO/KRB5 bind
696 static ADS_STATUS ads_sasl_spnego_rawkrb5_bind(ADS_STRUCT *ads, const char *principal)
698 DATA_BLOB blob = data_blob_null;
699 struct berval cred, *scred = NULL;
700 DATA_BLOB session_key = data_blob_null;
701 int rc;
703 if (ads->ldap.wrap_type > ADS_SASLWRAP_TYPE_PLAIN) {
704 return ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED);
707 rc = spnego_gen_negTokenTarg(principal, ads->auth.time_offset, &blob, &session_key, 0,
708 &ads->auth.tgs_expire);
710 if (rc) {
711 return ADS_ERROR_KRB5(rc);
714 /* now send the auth packet and we should be done */
715 cred.bv_val = (char *)blob.data;
716 cred.bv_len = blob.length;
718 rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
720 data_blob_free(&blob);
721 data_blob_free(&session_key);
722 if(scred)
723 ber_bvfree(scred);
725 return ADS_ERROR(rc);
728 static ADS_STATUS ads_sasl_spnego_krb5_bind(ADS_STRUCT *ads,
729 struct ads_service_principal *p)
731 #ifdef HAVE_GSSAPI
733 * we only use the gsskrb5 based implementation
734 * when sasl sign or seal is requested.
736 * This has the following reasons:
737 * - it's likely that the gssapi krb5 mech implementation
738 * doesn't support to negotiate plain connections
739 * - the ads_sasl_spnego_rawkrb5_bind is more robust
740 * against clock skew errors
742 if (ads->ldap.wrap_type > ADS_SASLWRAP_TYPE_PLAIN) {
743 return ads_sasl_spnego_gsskrb5_bind(ads, p->name);
745 #endif
746 return ads_sasl_spnego_rawkrb5_bind(ads, p->string);
748 #endif /* HAVE_KRB5 */
751 this performs a SASL/SPNEGO bind
753 static ADS_STATUS ads_sasl_spnego_bind(ADS_STRUCT *ads)
755 struct berval *scred=NULL;
756 int rc, i;
757 ADS_STATUS status;
758 DATA_BLOB blob;
759 char *given_principal = NULL;
760 char *OIDs[ASN1_MAX_OIDS];
761 #ifdef HAVE_KRB5
762 bool got_kerberos_mechanism = False;
763 #endif
765 rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSS-SPNEGO", NULL, NULL, NULL, &scred);
767 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
768 status = ADS_ERROR(rc);
769 goto failed;
772 blob = data_blob(scred->bv_val, scred->bv_len);
774 ber_bvfree(scred);
776 #if 0
777 file_save("sasl_spnego.dat", blob.data, blob.length);
778 #endif
780 /* the server sent us the first part of the SPNEGO exchange in the negprot
781 reply */
782 if (!spnego_parse_negTokenInit(blob, OIDs, &given_principal)) {
783 data_blob_free(&blob);
784 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
785 goto failed;
787 data_blob_free(&blob);
789 /* make sure the server understands kerberos */
790 for (i=0;OIDs[i];i++) {
791 DEBUG(3,("ads_sasl_spnego_bind: got OID=%s\n", OIDs[i]));
792 #ifdef HAVE_KRB5
793 if (strcmp(OIDs[i], OID_KERBEROS5_OLD) == 0 ||
794 strcmp(OIDs[i], OID_KERBEROS5) == 0) {
795 got_kerberos_mechanism = True;
797 #endif
798 talloc_free(OIDs[i]);
800 DEBUG(3,("ads_sasl_spnego_bind: got server principal name = %s\n", given_principal));
802 #ifdef HAVE_KRB5
803 if (!(ads->auth.flags & ADS_AUTH_DISABLE_KERBEROS) &&
804 got_kerberos_mechanism)
806 struct ads_service_principal p;
808 status = ads_generate_service_principal(ads, given_principal, &p);
809 TALLOC_FREE(given_principal);
810 if (!ADS_ERR_OK(status)) {
811 return status;
814 status = ads_sasl_spnego_krb5_bind(ads, &p);
815 if (ADS_ERR_OK(status)) {
816 ads_free_service_principal(&p);
817 return status;
820 DEBUG(10,("ads_sasl_spnego_krb5_bind failed with: %s, "
821 "calling kinit\n", ads_errstr(status)));
823 status = ADS_ERROR_KRB5(ads_kinit_password(ads));
825 if (ADS_ERR_OK(status)) {
826 status = ads_sasl_spnego_krb5_bind(ads, &p);
827 if (!ADS_ERR_OK(status)) {
828 DEBUG(0,("kinit succeeded but "
829 "ads_sasl_spnego_krb5_bind failed: %s\n",
830 ads_errstr(status)));
834 ads_free_service_principal(&p);
836 /* only fallback to NTLMSSP if allowed */
837 if (ADS_ERR_OK(status) ||
838 !(ads->auth.flags & ADS_AUTH_ALLOW_NTLMSSP)) {
839 return status;
841 } else
842 #endif
844 TALLOC_FREE(given_principal);
847 /* lets do NTLMSSP ... this has the big advantage that we don't need
848 to sync clocks, and we don't rely on special versions of the krb5
849 library for HMAC_MD4 encryption */
850 return ads_sasl_spnego_ntlmssp_bind(ads);
852 failed:
853 return status;
856 #ifdef HAVE_GSSAPI
857 #define MAX_GSS_PASSES 3
859 /* this performs a SASL/gssapi bind
860 we avoid using cyrus-sasl to make Samba more robust. cyrus-sasl
861 is very dependent on correctly configured DNS whereas
862 this routine is much less fragile
863 see RFC2078 and RFC2222 for details
865 static ADS_STATUS ads_sasl_gssapi_do_bind(ADS_STRUCT *ads, const gss_name_t serv_name)
867 uint32 minor_status;
868 gss_ctx_id_t context_handle = GSS_C_NO_CONTEXT;
869 gss_OID mech_type = GSS_C_NULL_OID;
870 gss_buffer_desc output_token, input_token;
871 uint32 req_flags, ret_flags;
872 int conf_state;
873 struct berval cred;
874 struct berval *scred = NULL;
875 int i=0;
876 int gss_rc, rc;
877 uint8 *p;
878 uint32 max_msg_size = ADS_SASL_WRAPPING_OUT_MAX_WRAPPED;
879 uint8 wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
880 ADS_STATUS status;
882 input_token.value = NULL;
883 input_token.length = 0;
886 * Note: here we always ask the gssapi for sign and seal
887 * as this is negotiated later after the mutal
888 * authentication
890 req_flags = GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG;
892 for (i=0; i < MAX_GSS_PASSES; i++) {
893 gss_rc = gss_init_sec_context(&minor_status,
894 GSS_C_NO_CREDENTIAL,
895 &context_handle,
896 serv_name,
897 mech_type,
898 req_flags,
900 NULL,
901 &input_token,
902 NULL,
903 &output_token,
904 &ret_flags,
905 NULL);
906 if (scred) {
907 ber_bvfree(scred);
908 scred = NULL;
910 if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) {
911 status = ADS_ERROR_GSS(gss_rc, minor_status);
912 goto failed;
915 cred.bv_val = (char *)output_token.value;
916 cred.bv_len = output_token.length;
918 rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSSAPI", &cred, NULL, NULL,
919 &scred);
920 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
921 status = ADS_ERROR(rc);
922 goto failed;
925 if (output_token.value) {
926 gss_release_buffer(&minor_status, &output_token);
929 if (scred) {
930 input_token.value = scred->bv_val;
931 input_token.length = scred->bv_len;
932 } else {
933 input_token.value = NULL;
934 input_token.length = 0;
937 if (gss_rc == 0) break;
940 gss_rc = gss_unwrap(&minor_status,context_handle,&input_token,&output_token,
941 &conf_state,NULL);
942 if (scred) {
943 ber_bvfree(scred);
944 scred = NULL;
946 if (gss_rc) {
947 status = ADS_ERROR_GSS(gss_rc, minor_status);
948 goto failed;
951 p = (uint8 *)output_token.value;
953 #if 0
954 file_save("sasl_gssapi.dat", output_token.value, output_token.length);
955 #endif
957 if (p) {
958 wrap_type = CVAL(p,0);
959 SCVAL(p,0,0);
960 max_msg_size = RIVAL(p,0);
963 gss_release_buffer(&minor_status, &output_token);
965 if (!(wrap_type & ads->ldap.wrap_type)) {
967 * the server doesn't supports the wrap
968 * type we want :-(
970 DEBUG(0,("The ldap sasl wrap type doesn't match wanted[%d] server[%d]\n",
971 ads->ldap.wrap_type, wrap_type));
972 DEBUGADD(0,("You may want to set the 'client ldap sasl wrapping' option\n"));
973 status = ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED);
974 goto failed;
977 /* 0x58 is the minimum windows accepts */
978 if (max_msg_size < 0x58) {
979 max_msg_size = 0x58;
982 output_token.length = 4;
983 output_token.value = SMB_MALLOC(output_token.length);
984 p = (uint8 *)output_token.value;
986 RSIVAL(p,0,max_msg_size);
987 SCVAL(p,0,ads->ldap.wrap_type);
990 * we used to add sprintf("dn:%s", ads->config.bind_path) here.
991 * but using ads->config.bind_path is the wrong! It should be
992 * the DN of the user object!
994 * w2k3 gives an error when we send an incorrect DN, but sending nothing
995 * is ok and matches the information flow used in GSS-SPNEGO.
998 gss_rc = gss_wrap(&minor_status, context_handle,0,GSS_C_QOP_DEFAULT,
999 &output_token, &conf_state,
1000 &input_token);
1001 if (gss_rc) {
1002 status = ADS_ERROR_GSS(gss_rc, minor_status);
1003 goto failed;
1006 free(output_token.value);
1008 cred.bv_val = (char *)input_token.value;
1009 cred.bv_len = input_token.length;
1011 rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSSAPI", &cred, NULL, NULL,
1012 &scred);
1013 gss_release_buffer(&minor_status, &input_token);
1014 status = ADS_ERROR(rc);
1015 if (!ADS_ERR_OK(status)) {
1016 goto failed;
1019 if (ads->ldap.wrap_type > ADS_SASLWRAP_TYPE_PLAIN) {
1020 gss_rc = gss_wrap_size_limit(&minor_status, context_handle,
1021 (ads->ldap.wrap_type == ADS_SASLWRAP_TYPE_SEAL),
1022 GSS_C_QOP_DEFAULT,
1023 max_msg_size, &ads->ldap.out.max_unwrapped);
1024 if (gss_rc) {
1025 status = ADS_ERROR_GSS(gss_rc, minor_status);
1026 goto failed;
1029 ads->ldap.out.sig_size = max_msg_size - ads->ldap.out.max_unwrapped;
1030 ads->ldap.in.min_wrapped = 0x2C; /* taken from a capture with LDAP unbind */
1031 ads->ldap.in.max_wrapped = max_msg_size;
1032 status = ads_setup_sasl_wrapping(ads, &ads_sasl_gssapi_ops, context_handle);
1033 if (!ADS_ERR_OK(status)) {
1034 DEBUG(0, ("ads_setup_sasl_wrapping() failed: %s\n",
1035 ads_errstr(status)));
1036 goto failed;
1038 /* make sure we don't free context_handle */
1039 context_handle = GSS_C_NO_CONTEXT;
1042 failed:
1044 if (context_handle != GSS_C_NO_CONTEXT)
1045 gss_delete_sec_context(&minor_status, &context_handle, GSS_C_NO_BUFFER);
1047 if(scred)
1048 ber_bvfree(scred);
1049 return status;
1052 static ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads)
1054 ADS_STATUS status;
1055 struct ads_service_principal p;
1057 status = ads_generate_service_principal(ads, NULL, &p);
1058 if (!ADS_ERR_OK(status)) {
1059 return status;
1062 status = ads_sasl_gssapi_do_bind(ads, p.name);
1063 if (ADS_ERR_OK(status)) {
1064 ads_free_service_principal(&p);
1065 return status;
1068 DEBUG(10,("ads_sasl_gssapi_do_bind failed with: %s, "
1069 "calling kinit\n", ads_errstr(status)));
1071 status = ADS_ERROR_KRB5(ads_kinit_password(ads));
1073 if (ADS_ERR_OK(status)) {
1074 status = ads_sasl_gssapi_do_bind(ads, p.name);
1077 ads_free_service_principal(&p);
1079 return status;
1082 #endif /* HAVE_GSSAPI */
1084 /* mapping between SASL mechanisms and functions */
1085 static struct {
1086 const char *name;
1087 ADS_STATUS (*fn)(ADS_STRUCT *);
1088 } sasl_mechanisms[] = {
1089 {"GSS-SPNEGO", ads_sasl_spnego_bind},
1090 #ifdef HAVE_GSSAPI
1091 {"GSSAPI", ads_sasl_gssapi_bind}, /* doesn't work with .NET RC1. No idea why */
1092 #endif
1093 {NULL, NULL}
1096 ADS_STATUS ads_sasl_bind(ADS_STRUCT *ads)
1098 const char *attrs[] = {"supportedSASLMechanisms", NULL};
1099 char **values;
1100 ADS_STATUS status;
1101 int i, j;
1102 LDAPMessage *res;
1104 /* get a list of supported SASL mechanisms */
1105 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
1106 if (!ADS_ERR_OK(status)) return status;
1108 values = ldap_get_values(ads->ldap.ld, res, "supportedSASLMechanisms");
1110 if (ads->auth.flags & ADS_AUTH_SASL_SEAL) {
1111 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SEAL;
1112 } else if (ads->auth.flags & ADS_AUTH_SASL_SIGN) {
1113 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SIGN;
1114 } else {
1115 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
1118 /* try our supported mechanisms in order */
1119 for (i=0;sasl_mechanisms[i].name;i++) {
1120 /* see if the server supports it */
1121 for (j=0;values && values[j];j++) {
1122 if (strcmp(values[j], sasl_mechanisms[i].name) == 0) {
1123 DEBUG(4,("Found SASL mechanism %s\n", values[j]));
1124 retry:
1125 status = sasl_mechanisms[i].fn(ads);
1126 if (status.error_type == ENUM_ADS_ERROR_LDAP &&
1127 status.err.rc == LDAP_STRONG_AUTH_REQUIRED &&
1128 ads->ldap.wrap_type == ADS_SASLWRAP_TYPE_PLAIN)
1130 DEBUG(3,("SASL bin got LDAP_STRONG_AUTH_REQUIRED "
1131 "retrying with signing enabled\n"));
1132 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SIGN;
1133 goto retry;
1135 ldap_value_free(values);
1136 ldap_msgfree(res);
1137 return status;
1142 ldap_value_free(values);
1143 ldap_msgfree(res);
1144 return ADS_ERROR(LDAP_AUTH_METHOD_NOT_SUPPORTED);
1147 #endif /* HAVE_LDAP */