#include "includes.h"
#include "../libcli/auth/spnego.h"
-#include "ntlmssp.h"
+#include "auth/gensec/gensec.h"
+#include "auth_generic.h"
+#include "ads.h"
+#include "smb_krb5.h"
#ifdef HAVE_LDAP
static ADS_STATUS ads_sasl_ntlmssp_wrap(ADS_STRUCT *ads, uint8 *buf, uint32 len)
{
- struct ntlmssp_state *ntlmssp_state =
- (struct ntlmssp_state *)ads->ldap.wrap_private_data;
- ADS_STATUS status;
+ struct gensec_security *gensec_security =
+ talloc_get_type_abort(ads->ldap.wrap_private_data,
+ struct gensec_security);
NTSTATUS nt_status;
- DATA_BLOB sig;
- uint8 *dptr = ads->ldap.out.buf + (4 + NTLMSSP_SIG_SIZE);
-
- /* copy the data to the right location */
- memcpy(dptr, buf, len);
-
- /* create the signature and may encrypt the data */
- if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_SEAL) {
- nt_status = ntlmssp_seal_packet(ntlmssp_state,
- dptr, len,
- dptr, len,
- &sig);
- } else {
- nt_status = ntlmssp_sign_packet(ntlmssp_state,
- dptr, len,
- dptr, len,
- &sig);
+ DATA_BLOB unwrapped, wrapped;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ unwrapped = data_blob_const(buf, len);
+
+ nt_status = gensec_wrap(gensec_security, frame, &unwrapped, &wrapped);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ TALLOC_FREE(frame);
+ return ADS_ERROR_NT(nt_status);
}
- status = ADS_ERROR_NT(nt_status);
- if (!ADS_ERR_OK(status)) return status;
- /* copy the signature to the right location */
- memcpy(ads->ldap.out.buf + 4,
- sig.data, NTLMSSP_SIG_SIZE);
+ if ((ads->ldap.out.size - 4) < wrapped.length) {
+ return ADS_ERROR_NT(NT_STATUS_INTERNAL_ERROR);
+ }
- data_blob_free(&sig);
+ /* copy the wrapped blob to the right location */
+ memcpy(ads->ldap.out.buf + 4, wrapped.data, wrapped.length);
/* set how many bytes must be written to the underlying socket */
- ads->ldap.out.left = 4 + NTLMSSP_SIG_SIZE + len;
+ ads->ldap.out.left = 4 + wrapped.length;
+
+ TALLOC_FREE(frame);
return ADS_SUCCESS;
}
static ADS_STATUS ads_sasl_ntlmssp_unwrap(ADS_STRUCT *ads)
{
- struct ntlmssp_state *ntlmssp_state =
- (struct ntlmssp_state *)ads->ldap.wrap_private_data;
- ADS_STATUS status;
+ struct gensec_security *gensec_security =
+ talloc_get_type_abort(ads->ldap.wrap_private_data,
+ struct gensec_security);
NTSTATUS nt_status;
- DATA_BLOB sig;
- uint8 *dptr = ads->ldap.in.buf + (4 + NTLMSSP_SIG_SIZE);
- uint32 dlen = ads->ldap.in.ofs - (4 + NTLMSSP_SIG_SIZE);
-
- /* wrap the signature into a DATA_BLOB */
- sig = data_blob_const(ads->ldap.in.buf + 4, NTLMSSP_SIG_SIZE);
-
- /* verify the signature and maybe decrypt the data */
- if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_SEAL) {
- nt_status = ntlmssp_unseal_packet(ntlmssp_state,
- dptr, dlen,
- dptr, dlen,
- &sig);
- } else {
- nt_status = ntlmssp_check_packet(ntlmssp_state,
- dptr, dlen,
- dptr, dlen,
- &sig);
+ DATA_BLOB unwrapped, wrapped;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ wrapped = data_blob_const(ads->ldap.in.buf + 4, ads->ldap.in.ofs - 4);
+
+ nt_status = gensec_unwrap(gensec_security, frame, &wrapped, &unwrapped);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ TALLOC_FREE(frame);
+ return ADS_ERROR_NT(nt_status);
}
- status = ADS_ERROR_NT(nt_status);
- if (!ADS_ERR_OK(status)) return status;
- /* set the amount of bytes for the upper layer and set the ofs to the data */
- ads->ldap.in.left = dlen;
- ads->ldap.in.ofs = 4 + NTLMSSP_SIG_SIZE;
+ if (wrapped.length < unwrapped.length) {
+ TALLOC_FREE(frame);
+ return ADS_ERROR_NT(NT_STATUS_INTERNAL_ERROR);
+ }
+
+ /* copy the wrapped blob to the right location */
+ memcpy(ads->ldap.in.buf + 4, unwrapped.data, unwrapped.length);
+
+ /* set how many bytes must be written to the underlying socket */
+ ads->ldap.in.left = unwrapped.length;
+ ads->ldap.in.ofs = 4;
+
+ TALLOC_FREE(frame);
return ADS_SUCCESS;
}
static void ads_sasl_ntlmssp_disconnect(ADS_STRUCT *ads)
{
- struct ntlmssp_state *ntlmssp_state =
- (struct ntlmssp_state *)ads->ldap.wrap_private_data;
+ struct gensec_security *gensec_security =
+ talloc_get_type_abort(ads->ldap.wrap_private_data,
+ struct gensec_security);
- ntlmssp_end(&ntlmssp_state);
+ TALLOC_FREE(gensec_security);
ads->ldap.wrap_ops = NULL;
ads->ldap.wrap_private_data = NULL;
NTSTATUS nt_status;
ADS_STATUS status;
int turn = 1;
- uint32 features = 0;
- struct ntlmssp_state *ntlmssp_state;
+ struct auth_generic_state *auth_generic_state;
- nt_status = ntlmssp_client_start(NULL,
- global_myname(),
- lp_workgroup(),
- lp_client_ntlmv2_auth(),
- &ntlmssp_state);
+ nt_status = auth_generic_client_prepare(NULL, &auth_generic_state);
if (!NT_STATUS_IS_OK(nt_status)) {
return ADS_ERROR_NT(nt_status);
}
- ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_SIGN;
- if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_username(ntlmssp_state, ads->auth.user_name))) {
+ if (!NT_STATUS_IS_OK(nt_status = auth_generic_set_username(auth_generic_state, ads->auth.user_name))) {
return ADS_ERROR_NT(nt_status);
}
- if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_domain(ntlmssp_state, ads->auth.realm))) {
+ if (!NT_STATUS_IS_OK(nt_status = auth_generic_set_domain(auth_generic_state, ads->auth.realm))) {
return ADS_ERROR_NT(nt_status);
}
- if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_password(ntlmssp_state, ads->auth.password))) {
+ if (!NT_STATUS_IS_OK(nt_status = auth_generic_set_password(auth_generic_state, ads->auth.password))) {
return ADS_ERROR_NT(nt_status);
}
switch (ads->ldap.wrap_type) {
case ADS_SASLWRAP_TYPE_SEAL:
- features = NTLMSSP_FEATURE_SIGN | NTLMSSP_FEATURE_SEAL;
+ gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_SIGN);
+ gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_SEAL);
break;
case ADS_SASLWRAP_TYPE_SIGN:
if (ads->auth.flags & ADS_AUTH_SASL_FORCE) {
- features = NTLMSSP_FEATURE_SIGN;
+ gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_SIGN);
} else {
/*
* windows servers are broken with sign only,
* so we need to use seal here too
*/
- features = NTLMSSP_FEATURE_SIGN | NTLMSSP_FEATURE_SEAL;
+ gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_SIGN);
+ gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_SEAL);
ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SEAL;
}
break;
break;
}
- ntlmssp_want_feature(ntlmssp_state, features);
+ nt_status = auth_generic_client_start(auth_generic_state, GENSEC_OID_NTLMSSP);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return ADS_ERROR_NT(nt_status);
+ }
blob_in = data_blob_null;
do {
- nt_status = ntlmssp_update(ntlmssp_state,
- blob_in, &blob_out);
+ nt_status = gensec_update(auth_generic_state->gensec_security,
+ talloc_tos(), NULL, blob_in, &blob_out);
data_blob_free(&blob_in);
if ((NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)
|| NT_STATUS_IS_OK(nt_status))
&& blob_out.length) {
if (turn == 1) {
+ const char *OIDs_ntlm[] = {OID_NTLMSSP, NULL};
/* and wrap it in a SPNEGO wrapper */
- msg1 = gen_negTokenInit(OID_NTLMSSP, blob_out);
+ msg1 = spnego_gen_negTokenInit(talloc_tos(),
+ OIDs_ntlm, &blob_out, NULL);
} else {
/* wrap it in SPNEGO */
- msg1 = spnego_gen_auth(blob_out);
+ msg1 = spnego_gen_auth(talloc_tos(), blob_out);
}
data_blob_free(&blob_out);
ber_bvfree(scred);
}
- ntlmssp_end(&ntlmssp_state);
+ TALLOC_FREE(auth_generic_state);
return ADS_ERROR(rc);
}
if (scred) {
} else {
- ntlmssp_end(&ntlmssp_state);
+ TALLOC_FREE(auth_generic_state);
data_blob_free(&blob_out);
return ADS_ERROR_NT(nt_status);
}
(rc == LDAP_SASL_BIND_IN_PROGRESS)) {
DATA_BLOB tmp_blob = data_blob_null;
/* the server might give us back two challenges */
- if (!spnego_parse_challenge(blob, &blob_in,
+ if (!spnego_parse_challenge(talloc_tos(), blob, &blob_in,
&tmp_blob)) {
- ntlmssp_end(&ntlmssp_state);
+ TALLOC_FREE(auth_generic_state);
data_blob_free(&blob);
DEBUG(3,("Failed to parse challenges\n"));
return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
}
data_blob_free(&tmp_blob);
} else if (rc == LDAP_SASL_BIND_IN_PROGRESS) {
- if (!spnego_parse_auth_response(blob, nt_status, OID_NTLMSSP,
+ if (!spnego_parse_auth_response(talloc_tos(), blob, nt_status, OID_NTLMSSP,
&blob_in)) {
- ntlmssp_end(&ntlmssp_state);
+ TALLOC_FREE(auth_generic_state);
data_blob_free(&blob);
DEBUG(3,("Failed to parse auth response\n"));
return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
turn++;
} while (rc == LDAP_SASL_BIND_IN_PROGRESS && !NT_STATUS_IS_OK(nt_status));
- /* we have a reference conter on ntlmssp_state, if we are signing
- then the state will be kept by the signing engine */
-
if (ads->ldap.wrap_type > ADS_SASLWRAP_TYPE_PLAIN) {
- ads->ldap.out.max_unwrapped = ADS_SASL_WRAPPING_OUT_MAX_WRAPPED - NTLMSSP_SIG_SIZE;
- ads->ldap.out.sig_size = NTLMSSP_SIG_SIZE;
+ uint32_t sig_size = gensec_sig_size(auth_generic_state->gensec_security, 0);
+ ads->ldap.out.max_unwrapped = ADS_SASL_WRAPPING_OUT_MAX_WRAPPED - sig_size;
+ ads->ldap.out.sig_size = sig_size;
ads->ldap.in.min_wrapped = ads->ldap.out.sig_size;
ads->ldap.in.max_wrapped = ADS_SASL_WRAPPING_IN_MAX_WRAPPED;
- status = ads_setup_sasl_wrapping(ads, &ads_sasl_ntlmssp_ops, ntlmssp_state);
+ status = ads_setup_sasl_wrapping(ads, &ads_sasl_ntlmssp_ops, auth_generic_state->gensec_security);
if (!ADS_ERR_OK(status)) {
DEBUG(0, ("ads_setup_sasl_wrapping() failed: %s\n",
ads_errstr(status)));
- ntlmssp_end(&ntlmssp_state);
+ TALLOC_FREE(auth_generic_state);
return status;
}
- } else {
- ntlmssp_end(&ntlmssp_state);
+ /* Only keep the gensec_security element around long-term */
+ talloc_steal(NULL, auth_generic_state->gensec_security);
}
+ TALLOC_FREE(auth_generic_state);
return ADS_ERROR(rc);
}
-#ifdef HAVE_GSSAPI
+#ifdef HAVE_KRB5
static ADS_STATUS ads_sasl_gssapi_wrap(ADS_STRUCT *ads, uint8 *buf, uint32 len)
{
gss_ctx_id_t context_handle = (gss_ctx_id_t)ads->ldap.wrap_private_data;
uint32 minor_status;
int gss_rc, rc;
gss_OID_desc krb5_mech_type =
- {9, CONST_DISCARD(char *, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02") };
+ {9, discard_const_p(char, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02") };
gss_OID mech_type = &krb5_mech_type;
gss_OID actual_mech_type = GSS_C_NULL_OID;
const char *spnego_mechs[] = {OID_KERBEROS5_OLD, OID_KERBEROS5, OID_NTLMSSP, NULL};
/* and wrap that in a shiny SPNEGO wrapper */
unwrapped = data_blob_const(output_token.value, output_token.length);
- wrapped = gen_negTokenTarg(spnego_mechs, unwrapped);
+ wrapped = spnego_gen_negTokenInit(talloc_tos(),
+ spnego_mechs, &unwrapped, NULL);
gss_release_buffer(&minor_status, &output_token);
if (unwrapped.length > wrapped.length) {
status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
wrapped = data_blob_null;
}
- ok = spnego_parse_auth_response(wrapped, NT_STATUS_OK,
+ ok = spnego_parse_auth_response(talloc_tos(), wrapped, NT_STATUS_OK,
OID_KERBEROS5_OLD,
&unwrapped);
if (scred) ber_bvfree(scred);
return status;
}
-#endif /* HAVE_GSSAPI */
+#endif /* HAVE_KRB5 */
#ifdef HAVE_KRB5
struct ads_service_principal {
char *string;
-#ifdef HAVE_GSSAPI
+#ifdef HAVE_KRB5
gss_name_t name;
#endif
};
{
SAFE_FREE(p->string);
-#ifdef HAVE_GSSAPI
+#ifdef HAVE_KRB5
if (p->name) {
uint32 minor_status;
gss_release_name(&minor_status, &p->name);
ZERO_STRUCTP(p);
}
+
+static ADS_STATUS ads_guess_service_principal(ADS_STRUCT *ads,
+ char **returned_principal)
+{
+ char *princ = NULL;
+
+ if (ads->server.realm && ads->server.ldap_server) {
+ char *server, *server_realm;
+
+ server = SMB_STRDUP(ads->server.ldap_server);
+ server_realm = SMB_STRDUP(ads->server.realm);
+
+ if (!server || !server_realm) {
+ SAFE_FREE(server);
+ SAFE_FREE(server_realm);
+ return ADS_ERROR(LDAP_NO_MEMORY);
+ }
+
+ strlower_m(server);
+ strupper_m(server_realm);
+ if (asprintf(&princ, "ldap/%s@%s", server, server_realm) == -1) {
+ SAFE_FREE(server);
+ SAFE_FREE(server_realm);
+ return ADS_ERROR(LDAP_NO_MEMORY);
+ }
+
+ SAFE_FREE(server);
+ SAFE_FREE(server_realm);
+
+ if (!princ) {
+ return ADS_ERROR(LDAP_NO_MEMORY);
+ }
+ } else if (ads->config.realm && ads->config.ldap_server_name) {
+ char *server, *server_realm;
+
+ server = SMB_STRDUP(ads->config.ldap_server_name);
+ server_realm = SMB_STRDUP(ads->config.realm);
+
+ if (!server || !server_realm) {
+ SAFE_FREE(server);
+ SAFE_FREE(server_realm);
+ return ADS_ERROR(LDAP_NO_MEMORY);
+ }
+
+ strlower_m(server);
+ strupper_m(server_realm);
+ if (asprintf(&princ, "ldap/%s@%s", server, server_realm) == -1) {
+ SAFE_FREE(server);
+ SAFE_FREE(server_realm);
+ return ADS_ERROR(LDAP_NO_MEMORY);
+ }
+
+ SAFE_FREE(server);
+ SAFE_FREE(server_realm);
+
+ if (!princ) {
+ return ADS_ERROR(LDAP_NO_MEMORY);
+ }
+ }
+
+ if (!princ) {
+ return ADS_ERROR(LDAP_PARAM_ERROR);
+ }
+
+ *returned_principal = princ;
+
+ return ADS_SUCCESS;
+}
+
static ADS_STATUS ads_generate_service_principal(ADS_STRUCT *ads,
const char *given_principal,
struct ads_service_principal *p)
{
ADS_STATUS status;
-#ifdef HAVE_GSSAPI
+#ifdef HAVE_KRB5
gss_buffer_desc input_name;
/* GSS_KRB5_NT_PRINCIPAL_NAME */
gss_OID_desc nt_principal =
- {10, CONST_DISCARD(char *, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x01")};
+ {10, discard_const_p(char, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x01")};
uint32 minor_status;
int gss_rc;
#endif
the principal name back in the first round of
the SASL bind reply. So we guess based on server
name and realm. --jerry */
- /* Also try best guess when we get the w2k8 ignore
- principal back - gd */
+ /* Also try best guess when we get the w2k8 ignore principal
+ back, or when we are configured to ignore it - gd,
+ abartlet */
- if (!given_principal ||
+ if (!lp_client_use_spnego_principal() ||
+ !given_principal ||
strequal(given_principal, ADS_IGNORE_PRINCIPAL)) {
status = ads_guess_service_principal(ads, &p->string);
}
}
-#ifdef HAVE_GSSAPI
+#ifdef HAVE_KRB5
input_name.value = p->string;
input_name.length = strlen(p->string);
return ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED);
}
- rc = spnego_gen_negTokenTarg(principal, ads->auth.time_offset, &blob, &session_key, 0,
+ rc = spnego_gen_krb5_negTokenInit(talloc_tos(), principal,
+ ads->auth.time_offset, &blob, &session_key, 0,
&ads->auth.tgs_expire);
if (rc) {
static ADS_STATUS ads_sasl_spnego_krb5_bind(ADS_STRUCT *ads,
struct ads_service_principal *p)
{
-#ifdef HAVE_GSSAPI
+#ifdef HAVE_KRB5
/*
* we only use the gsskrb5 based implementation
* when sasl sign or seal is requested.
/* the server sent us the first part of the SPNEGO exchange in the negprot
reply */
- if (!spnego_parse_negTokenInit(blob, OIDs, &given_principal)) {
+ if (!spnego_parse_negTokenInit(talloc_tos(), blob, OIDs, &given_principal, NULL) ||
+ OIDs[0] == NULL) {
data_blob_free(&blob);
status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
goto failed;
return status;
}
-#ifdef HAVE_GSSAPI
+#ifdef HAVE_KRB5
#define MAX_GSS_PASSES 3
/* this performs a SASL/gssapi bind
output_token.length = 4;
output_token.value = SMB_MALLOC(output_token.length);
+ if (!output_token.value) {
+ output_token.length = 0;
+ status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+ goto failed;
+ }
p = (uint8 *)output_token.value;
RSIVAL(p,0,max_msg_size);
*/
gss_rc = gss_wrap(&minor_status, context_handle,0,GSS_C_QOP_DEFAULT,
- &output_token, &conf_state,
- &input_token);
+ &output_token, /* used as *input* here. */
+ &conf_state,
+ &input_token); /* Used as *output* here. */
if (gss_rc) {
status = ADS_ERROR_GSS(gss_rc, minor_status);
+ output_token.length = 0;
+ SAFE_FREE(output_token.value);
goto failed;
}
- free(output_token.value);
+ /* We've finished with output_token. */
+ SAFE_FREE(output_token.value);
+ output_token.length = 0;
cred.bv_val = (char *)input_token.value;
cred.bv_len = input_token.length;
return status;
}
-#endif /* HAVE_GSSAPI */
+#endif /* HAVE_KRB5 */
/* mapping between SASL mechanisms and functions */
static struct {
ADS_STATUS (*fn)(ADS_STRUCT *);
} sasl_mechanisms[] = {
{"GSS-SPNEGO", ads_sasl_spnego_bind},
-#ifdef HAVE_GSSAPI
+#ifdef HAVE_KRB5
{"GSSAPI", ads_sasl_gssapi_bind}, /* doesn't work with .NET RC1. No idea why */
#endif
{NULL, NULL}
for (j=0;values && values[j];j++) {
if (strcmp(values[j], sasl_mechanisms[i].name) == 0) {
DEBUG(4,("Found SASL mechanism %s\n", values[j]));
+retry:
status = sasl_mechanisms[i].fn(ads);
+ if (status.error_type == ENUM_ADS_ERROR_LDAP &&
+ status.err.rc == LDAP_STRONG_AUTH_REQUIRED &&
+ ads->ldap.wrap_type == ADS_SASLWRAP_TYPE_PLAIN)
+ {
+ DEBUG(3,("SASL bin got LDAP_STRONG_AUTH_REQUIRED "
+ "retrying with signing enabled\n"));
+ ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SIGN;
+ goto retry;
+ }
ldap_value_free(values);
ldap_msgfree(res);
return status;