struct composite_context *wb_init_domain_send(TALLOC_CTX *mem_ctx, struct wbsrv_service *service, struct wb_dom_info *dom_info) { struct composite_context *result, *ctx; struct init_domain_state *state; result = composite_create(mem_ctx, service->task->event_ctx); if (result == NULL) goto failed; state = talloc_zero(result, struct init_domain_state); if (state == NULL) goto failed; state->ctx = result; result->private_data = state; state->service = service; state->domain = talloc(state, struct wbsrv_domain); if (state->domain == NULL) goto failed; state->domain->service = service; state->domain->info = talloc_reference(state->domain, dom_info); if (state->domain->info == NULL) goto failed; state->domain->dc_name = dom_info->dc->name; state->domain->dc_address = dom_info->dc->address; state->domain->libnet_ctx = libnet_context_init(service->task->event_ctx, service->task->lp_ctx); if (state->domain->libnet_ctx == NULL) goto failed; talloc_steal(state->domain, state->domain->libnet_ctx); /* Create a credentials structure */ state->domain->libnet_ctx->cred = cli_credentials_init(state->domain); if (state->domain->libnet_ctx->cred == NULL) goto failed; cli_credentials_set_conf(state->domain->libnet_ctx->cred, service->task->lp_ctx); /* Connect the machine account to the credentials */ state->ctx->status = cli_credentials_set_machine_account(state->domain->libnet_ctx->cred, state->domain->libnet_ctx->lp_ctx); if (!NT_STATUS_IS_OK(state->ctx->status)) goto failed; state->domain->netlogon_binding = init_domain_binding(state, &ndr_table_netlogon); state->domain->netlogon_pipe = NULL; state->domain->netlogon_queue = tevent_queue_create(state->domain, "netlogon_queue"); if (state->domain->netlogon_queue == NULL) goto failed; /* We start the queue when the connection is usable */ tevent_queue_stop(state->domain->netlogon_queue); if ((!cli_credentials_is_anonymous(state->domain->libnet_ctx->cred)) && ((lpcfg_server_role(service->task->lp_ctx) == ROLE_DOMAIN_MEMBER) || (lpcfg_server_role(service->task->lp_ctx) == ROLE_ACTIVE_DIRECTORY_DC)) && (dom_sid_equal(state->domain->info->sid, state->service->primary_sid))) { uint32_t flags = DCERPC_SCHANNEL | DCERPC_SCHANNEL_AUTO; /* For debugging, it can be a real pain if all the traffic is encrypted */ if (lpcfg_winbind_sealed_pipes(service->task->lp_ctx)) { flags |= DCERPC_SIGN | DCERPC_SEAL; } else { flags |= DCERPC_SIGN; } state->ctx->status = dcerpc_binding_set_flags(state->domain->netlogon_binding, flags, 0); if (!NT_STATUS_IS_OK(state->ctx->status)) goto failed; } /* No encryption on anonymous pipes */ ctx = dcerpc_pipe_connect_b_send(state, state->domain->netlogon_binding, &ndr_table_netlogon, state->domain->libnet_ctx->cred, service->task->event_ctx, service->task->lp_ctx); if (composite_nomem(ctx, state->ctx)) { goto failed; } composite_continue(state->ctx, ctx, init_domain_recv_netlogonpipe, state); return result; failed: talloc_free(result); return NULL; }
/* handler for completion of a smbcli_request sub-request */ static void request_handler(struct smbcli_request *req) { struct composite_context *c = (struct composite_context *)req->async.private_data; struct sesssetup_state *state = talloc_get_type(c->private_data, struct sesssetup_state); struct smbcli_session *session = req->session; DATA_BLOB session_key = data_blob(NULL, 0); DATA_BLOB null_data_blob = data_blob(NULL, 0); NTSTATUS session_key_err, nt_status; struct smbcli_request *check_req = NULL; const char *os = NULL; const char *lanman = NULL; if (req->sign_caller_checks) { req->do_not_free = true; check_req = req; } state->remote_status = smb_raw_sesssetup_recv(req, state, &state->setup); c->status = state->remote_status; state->req = NULL; /* * we only need to check the signature if the * NT_STATUS_OK is returned */ if (!NT_STATUS_IS_OK(state->remote_status)) { talloc_free(check_req); check_req = NULL; } switch (state->setup.old.level) { case RAW_SESSSETUP_OLD: state->io->out.vuid = state->setup.old.out.vuid; /* This doesn't work, as this only happens on old * protocols, where this comparison won't match. */ if (NT_STATUS_EQUAL(c->status, NT_STATUS_LOGON_FAILURE)) { /* we neet to reset the vuid for a new try */ session->vuid = 0; if (cli_credentials_wrong_password(state->io->in.credentials)) { nt_status = session_setup_old(c, session, state->io, &state->req); if (NT_STATUS_IS_OK(nt_status)) { talloc_free(check_req); c->status = nt_status; composite_continue_smb(c, state->req, request_handler, c); return; } } } os = state->setup.old.out.os; lanman = state->setup.old.out.lanman; break; case RAW_SESSSETUP_NT1: state->io->out.vuid = state->setup.nt1.out.vuid; if (NT_STATUS_EQUAL(c->status, NT_STATUS_LOGON_FAILURE)) { /* we neet to reset the vuid for a new try */ session->vuid = 0; if (cli_credentials_wrong_password(state->io->in.credentials)) { nt_status = session_setup_nt1(c, session, state->io, &state->req); if (NT_STATUS_IS_OK(nt_status)) { talloc_free(check_req); c->status = nt_status; composite_continue_smb(c, state->req, request_handler, c); return; } } } os = state->setup.nt1.out.os; lanman = state->setup.nt1.out.lanman; break; case RAW_SESSSETUP_SPNEGO: state->io->out.vuid = state->setup.spnego.out.vuid; if (NT_STATUS_EQUAL(c->status, NT_STATUS_LOGON_FAILURE)) { /* we need to reset the vuid for a new try */ session->vuid = 0; if (cli_credentials_wrong_password(state->io->in.credentials)) { nt_status = session_setup_spnego(c, session, state->io, &state->req); if (NT_STATUS_IS_OK(nt_status)) { talloc_free(check_req); c->status = nt_status; composite_continue_smb(c, state->req, request_handler, c); return; } } } if (!NT_STATUS_EQUAL(c->status, NT_STATUS_MORE_PROCESSING_REQUIRED) && !NT_STATUS_IS_OK(c->status)) { break; } if (NT_STATUS_EQUAL(state->gensec_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { /* The status value here, from the earlier pass at GENSEC is * vital to the security of the system. Even if the other end * accepts, if GENSEC claims 'MORE_PROCESSING_REQUIRED' then * you must keep feeding it blobs, or else the remote * host/attacker might avoid mutal authentication * requirements */ state->gensec_status = gensec_update(session->gensec, state, state->setup.spnego.out.secblob, &state->setup.spnego.in.secblob); c->status = state->gensec_status; if (!NT_STATUS_EQUAL(c->status, NT_STATUS_MORE_PROCESSING_REQUIRED) && !NT_STATUS_IS_OK(c->status)) { break; } } else { state->setup.spnego.in.secblob = data_blob(NULL, 0); } if (NT_STATUS_IS_OK(state->remote_status)) { if (state->setup.spnego.in.secblob.length) { c->status = NT_STATUS_INTERNAL_ERROR; break; } session_key_err = gensec_session_key(session->gensec, &session_key); if (NT_STATUS_IS_OK(session_key_err)) { set_user_session_key(session, &session_key); smbcli_transport_simple_set_signing(session->transport, session_key, null_data_blob); } } if (state->setup.spnego.in.secblob.length) { /* * set the session->vuid value only for calling * smb_raw_sesssetup_send() */ uint16_t vuid = session->vuid; session->vuid = state->io->out.vuid; state->req = smb_raw_sesssetup_send(session, &state->setup); session->vuid = vuid; if (state->req) { state->req->sign_caller_checks = true; } composite_continue_smb(c, state->req, request_handler, c); return; } os = state->setup.spnego.out.os; lanman = state->setup.spnego.out.lanman; break; case RAW_SESSSETUP_SMB2: c->status = NT_STATUS_INTERNAL_ERROR; break; } if (check_req) { check_req->sign_caller_checks = false; if (!smbcli_request_check_sign_mac(check_req)) { c->status = NT_STATUS_ACCESS_DENIED; } talloc_free(check_req); check_req = NULL; } /* enforce the local signing required flag */ if (NT_STATUS_IS_OK(c->status) && !cli_credentials_is_anonymous(state->io->in.credentials)) { if (!session->transport->negotiate.sign_info.doing_signing && session->transport->negotiate.sign_info.mandatory_signing) { DEBUG(0, ("SMB signing required, but server does not support it\n")); c->status = NT_STATUS_ACCESS_DENIED; } } if (!NT_STATUS_IS_OK(c->status)) { composite_error(c, c->status); return; } if (os) { session->os = talloc_strdup(session, os); if (composite_nomem(session->os, c)) return; } else { session->os = NULL; } if (lanman) { session->lanman = talloc_strdup(session, lanman); if (composite_nomem(session->lanman, c)) return; } else { session->lanman = NULL; } composite_done(c); }
static PyObject *py_creds_is_anonymous(PyObject *self, PyObject *unused) { return PyBool_FromLong(cli_credentials_is_anonymous(PyCredentials_AsCliCredentials(self))); }
/* Get the keytab (actually, a container containing the krb5_keytab) * attached to this context. If this hasn't been done or set before, * it will be generated from the password. */ _PUBLIC_ int cli_credentials_get_keytab(struct cli_credentials *cred, struct loadparm_context *lp_ctx, struct keytab_container **_ktc) { krb5_error_code ret; struct keytab_container *ktc; struct smb_krb5_context *smb_krb5_context; const char *keytab_name; krb5_keytab keytab; TALLOC_CTX *mem_ctx; if (cred->keytab_obtained >= (MAX(cred->principal_obtained, cred->username_obtained))) { *_ktc = cred->keytab; return 0; } if (cli_credentials_is_anonymous(cred)) { return EINVAL; } ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context); if (ret) { return ret; } mem_ctx = talloc_new(cred); if (!mem_ctx) { return ENOMEM; } ret = smb_krb5_create_memory_keytab(mem_ctx, smb_krb5_context->krb5_context, cli_credentials_get_password(cred), cli_credentials_get_username(cred), cli_credentials_get_realm(cred), cli_credentials_get_kvno(cred), &keytab, &keytab_name); if (ret) { talloc_free(mem_ctx); return ret; } ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context, keytab, keytab_name, &ktc); if (ret) { talloc_free(mem_ctx); return ret; } cred->keytab_obtained = (MAX(cred->principal_obtained, cred->username_obtained)); /* We make this keytab up based on a password. Therefore * match-by-key is acceptable, we can't match on the wrong * principal */ ktc->password_based = true; talloc_steal(cred, ktc); cred->keytab = ktc; *_ktc = cred->keytab; talloc_free(mem_ctx); return ret; }
_PUBLIC_ int cli_credentials_get_named_ccache(struct cli_credentials *cred, struct tevent_context *event_ctx, struct loadparm_context *lp_ctx, char *ccache_name, struct ccache_container **ccc, const char **error_string) { krb5_error_code ret; enum credentials_obtained obtained; if (cred->machine_account_pending) { cli_credentials_set_machine_account(cred, lp_ctx); } if (cred->ccache_obtained >= cred->ccache_threshold && cred->ccache_obtained > CRED_UNINITIALISED) { time_t lifetime; bool expired = false; ret = smb_krb5_cc_get_lifetime(cred->ccache->smb_krb5_context->krb5_context, cred->ccache->ccache, &lifetime); if (ret == KRB5_CC_END) { /* If we have a particular ccache set, without * an initial ticket, then assume there is a * good reason */ } else if (ret == 0) { if (lifetime == 0) { DEBUG(3, ("Ticket in credentials cache for %s expired, will refresh\n", cli_credentials_get_principal(cred, cred))); expired = true; } else if (lifetime < 300) { DEBUG(3, ("Ticket in credentials cache for %s will shortly expire (%u secs), will refresh\n", cli_credentials_get_principal(cred, cred), (unsigned int)lifetime)); expired = true; } } else { (*error_string) = talloc_asprintf(cred, "failed to get ccache lifetime: %s\n", smb_get_krb5_error_message(cred->ccache->smb_krb5_context->krb5_context, ret, cred)); return ret; } DEBUG(5, ("Ticket in credentials cache for %s will expire in %u secs\n", cli_credentials_get_principal(cred, cred), (unsigned int)lifetime)); if (!expired) { *ccc = cred->ccache; return 0; } } if (cli_credentials_is_anonymous(cred)) { (*error_string) = "Cannot get anonymous kerberos credentials"; return EINVAL; } ret = cli_credentials_new_ccache(cred, lp_ctx, ccache_name, ccc, error_string); if (ret) { return ret; } ret = kinit_to_ccache(cred, cred, (*ccc)->smb_krb5_context, event_ctx, (*ccc)->ccache, &obtained, error_string); if (ret) { return ret; } ret = cli_credentials_set_from_ccache(cred, *ccc, obtained, error_string); cred->ccache = *ccc; cred->ccache_obtained = cred->principal_obtained; if (ret) { return ret; } cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained); return 0; }
/* Get the keytab (actually, a container containing the krb5_keytab) * attached to this context. If this hasn't been done or set before, * it will be generated from the password. */ _PUBLIC_ int cli_credentials_get_keytab(struct cli_credentials *cred, struct loadparm_context *lp_ctx, struct keytab_container **_ktc) { krb5_error_code ret; struct keytab_container *ktc; struct smb_krb5_context *smb_krb5_context; const char *keytab_name; krb5_keytab keytab; TALLOC_CTX *mem_ctx; const char *username = cli_credentials_get_username(cred); const char *realm = cli_credentials_get_realm(cred); const char *error_string; const char *salt_principal; if (cred->keytab_obtained >= (MAX(cred->principal_obtained, cred->username_obtained))) { *_ktc = cred->keytab; return 0; } if (cli_credentials_is_anonymous(cred)) { return EINVAL; } ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context); if (ret) { return ret; } mem_ctx = talloc_new(cred); if (!mem_ctx) { return ENOMEM; } /* * FIXME: Currently there is no better way than to create the correct * salt principal by checking if the username ends with a '$'. It would * be better if it is part of the credentials. */ ret = smb_krb5_create_salt_principal(mem_ctx, username, realm, &salt_principal, &error_string); if (ret) { talloc_free(mem_ctx); return ret; } ret = smb_krb5_create_memory_keytab(mem_ctx, smb_krb5_context->krb5_context, cli_credentials_get_password(cred), username, realm, salt_principal, cli_credentials_get_kvno(cred), &keytab, &keytab_name); if (ret) { talloc_free(mem_ctx); return ret; } ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context, keytab, keytab_name, &ktc); if (ret) { talloc_free(mem_ctx); return ret; } cred->keytab_obtained = (MAX(cred->principal_obtained, cred->username_obtained)); /* We make this keytab up based on a password. Therefore * match-by-key is acceptable, we can't match on the wrong * principal */ ktc->password_based = true; talloc_steal(cred, ktc); cred->keytab = ktc; *_ktc = cred->keytab; talloc_free(mem_ctx); return ret; }
struct composite_context *wb_init_domain_send(TALLOC_CTX *mem_ctx, struct wbsrv_service *service, struct wb_dom_info *dom_info) { struct composite_context *result, *ctx; struct init_domain_state *state; result = composite_create(mem_ctx, service->task->event_ctx); if (result == NULL) goto failed; state = talloc_zero(result, struct init_domain_state); if (state == NULL) goto failed; state->ctx = result; result->private_data = state; state->service = service; state->domain = talloc(state, struct wbsrv_domain); if (state->domain == NULL) goto failed; state->domain->info = talloc_reference(state->domain, dom_info); if (state->domain->info == NULL) goto failed; /* Caller should check, but to be safe: */ if (dom_info->num_dcs < 1) { goto failed; } /* For now, we just pick the first. The next step will be to * walk the entire list. Also need to fix finddcs() to return * the entire list */ state->domain->dc_name = dom_info->dcs[0].name; state->domain->dc_address = dom_info->dcs[0].address; state->domain->libnet_ctx = libnet_context_init(service->task->event_ctx, service->task->lp_ctx); /* Create a credentials structure */ state->domain->libnet_ctx->cred = cli_credentials_init(state->domain); if (state->domain->libnet_ctx->cred == NULL) goto failed; cli_credentials_set_conf(state->domain->libnet_ctx->cred, service->task->lp_ctx); /* Connect the machine account to the credentials */ state->ctx->status = cli_credentials_set_machine_account(state->domain->libnet_ctx->cred, state->domain->libnet_ctx->lp_ctx); if (!NT_STATUS_IS_OK(state->ctx->status)) goto failed; state->domain->netlogon_binding = init_domain_binding(state, &ndr_table_netlogon); state->domain->netlogon_pipe = NULL; if ((!cli_credentials_is_anonymous(state->domain->libnet_ctx->cred)) && ((lp_server_role(service->task->lp_ctx) == ROLE_DOMAIN_MEMBER) || (lp_server_role(service->task->lp_ctx) == ROLE_DOMAIN_CONTROLLER)) && (dom_sid_equal(state->domain->info->sid, state->service->primary_sid))) { state->domain->netlogon_binding->flags |= DCERPC_SCHANNEL; /* For debugging, it can be a real pain if all the traffic is encrypted */ if (lp_winbind_sealed_pipes(service->task->lp_ctx)) { state->domain->netlogon_binding->flags |= (DCERPC_SIGN | DCERPC_SEAL ); } else { state->domain->netlogon_binding->flags |= (DCERPC_SIGN); } } /* No encryption on anonymous pipes */ ctx = dcerpc_pipe_connect_b_send(state, state->domain->netlogon_binding, &ndr_table_netlogon, state->domain->libnet_ctx->cred, service->task->event_ctx, service->task->lp_ctx); if (composite_nomem(ctx, state->ctx)) { goto failed; } composite_continue(state->ctx, ctx, init_domain_recv_netlogonpipe, state); return result; failed: talloc_free(result); return NULL; }
NTSTATUS ntlmssp_client_challenge(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx, const DATA_BLOB in, DATA_BLOB *out) { struct gensec_ntlmssp_context *gensec_ntlmssp = talloc_get_type_abort(gensec_security->private_data, struct gensec_ntlmssp_context); struct ntlmssp_state *ntlmssp_state = gensec_ntlmssp->ntlmssp_state; uint32_t chal_flags, ntlmssp_command, unkn1 = 0, unkn2 = 0; DATA_BLOB server_domain_blob; DATA_BLOB challenge_blob; DATA_BLOB target_info = data_blob(NULL, 0); char *server_domain; const char *chal_parse_string; const char *chal_parse_string_short = NULL; const char *auth_gen_string; DATA_BLOB lm_response = data_blob(NULL, 0); DATA_BLOB nt_response = data_blob(NULL, 0); DATA_BLOB session_key = data_blob(NULL, 0); DATA_BLOB lm_session_key = data_blob(NULL, 0); DATA_BLOB encrypted_session_key = data_blob(NULL, 0); NTSTATUS nt_status; int flags = 0; const char *user = NULL, *domain = NULL, *workstation = NULL; bool is_anonymous = false; const DATA_BLOB version_blob = ntlmssp_version_blob(); TALLOC_CTX *mem_ctx = talloc_new(out_mem_ctx); if (!mem_ctx) { return NT_STATUS_NO_MEMORY; } if (!msrpc_parse(mem_ctx, &in, "CdBd", "NTLMSSP", &ntlmssp_command, &server_domain_blob, &chal_flags)) { DEBUG(1, ("Failed to parse the NTLMSSP Challenge: (#1)\n")); dump_data(2, in.data, in.length); talloc_free(mem_ctx); return NT_STATUS_INVALID_PARAMETER; } data_blob_free(&server_domain_blob); DEBUG(3, ("Got challenge flags:\n")); debug_ntlmssp_flags(chal_flags); ntlmssp_handle_neg_flags(ntlmssp_state, chal_flags, ntlmssp_state->allow_lm_key); if (ntlmssp_state->unicode) { if (chal_flags & NTLMSSP_NEGOTIATE_TARGET_INFO) { chal_parse_string = "CdUdbddB"; } else { chal_parse_string = "CdUdbdd"; chal_parse_string_short = "CdUdb"; } auth_gen_string = "CdBBUUUBdb"; } else { if (chal_flags & NTLMSSP_NEGOTIATE_TARGET_INFO) { chal_parse_string = "CdAdbddB"; } else { chal_parse_string = "CdAdbdd"; chal_parse_string_short = "CdAdb"; } auth_gen_string = "CdBBAAABdb"; } if (!msrpc_parse(mem_ctx, &in, chal_parse_string, "NTLMSSP", &ntlmssp_command, &server_domain, &chal_flags, &challenge_blob, 8, &unkn1, &unkn2, &target_info)) { bool ok = false; DEBUG(1, ("Failed to parse the NTLMSSP Challenge: (#2)\n")); if (chal_parse_string_short != NULL) { /* * In the case where NTLMSSP_NEGOTIATE_TARGET_INFO * is not used, some NTLMSSP servers don't return * the unused unkn1 and unkn2 fields. * See bug: * https://bugzilla.samba.org/show_bug.cgi?id=10016 * for packet traces. * Try and parse again without them. */ ok = msrpc_parse(mem_ctx, &in, chal_parse_string_short, "NTLMSSP", &ntlmssp_command, &server_domain, &chal_flags, &challenge_blob, 8); if (!ok) { DEBUG(1, ("Failed to short parse " "the NTLMSSP Challenge: (#2)\n")); } } if (!ok) { dump_data(2, in.data, in.length); talloc_free(mem_ctx); return NT_STATUS_INVALID_PARAMETER; } } if (chal_flags & NTLMSSP_TARGET_TYPE_SERVER) { ntlmssp_state->server.is_standalone = true; } else { ntlmssp_state->server.is_standalone = false; } /* TODO: parse struct_blob and fill in the rest */ ntlmssp_state->server.netbios_name = ""; ntlmssp_state->server.netbios_domain = talloc_move(ntlmssp_state, &server_domain); ntlmssp_state->server.dns_name = ""; ntlmssp_state->server.dns_domain = ""; if (challenge_blob.length != 8) { talloc_free(mem_ctx); return NT_STATUS_INVALID_PARAMETER; } is_anonymous = cli_credentials_is_anonymous(gensec_security->credentials); cli_credentials_get_ntlm_username_domain(gensec_security->credentials, mem_ctx, &user, &domain); workstation = cli_credentials_get_workstation(gensec_security->credentials); if (user == NULL) { DEBUG(10, ("User is NULL, returning INVALID_PARAMETER\n")); return NT_STATUS_INVALID_PARAMETER; } if (domain == NULL) { DEBUG(10, ("Domain is NULL, returning INVALID_PARAMETER\n")); return NT_STATUS_INVALID_PARAMETER; } if (workstation == NULL) { DEBUG(10, ("Workstation is NULL, returning INVALID_PARAMETER\n")); return NT_STATUS_INVALID_PARAMETER; } if (is_anonymous) { ntlmssp_state->neg_flags |= NTLMSSP_ANONYMOUS; /* * don't use the ccache for anonymous auth */ ntlmssp_state->use_ccache = false; } if (ntlmssp_state->use_ccache) { struct samr_Password *nt_hash = NULL; /* * If we have a password given we don't * use the ccache */ nt_hash = cli_credentials_get_nt_hash(gensec_security->credentials, mem_ctx); if (nt_hash != NULL) { ZERO_STRUCTP(nt_hash); TALLOC_FREE(nt_hash); ntlmssp_state->use_ccache = false; } } if (ntlmssp_state->use_ccache) { struct wbcCredentialCacheParams params; struct wbcCredentialCacheInfo *info = NULL; struct wbcAuthErrorInfo *error = NULL; struct wbcNamedBlob auth_blobs[1]; const struct wbcBlob *wbc_auth_blob = NULL; const struct wbcBlob *wbc_session_key = NULL; wbcErr wbc_status; int i; params.account_name = user; params.domain_name = domain; params.level = WBC_CREDENTIAL_CACHE_LEVEL_NTLMSSP; auth_blobs[0].name = "challenge_blob"; auth_blobs[0].flags = 0; auth_blobs[0].blob.data = in.data; auth_blobs[0].blob.length = in.length; params.num_blobs = ARRAY_SIZE(auth_blobs); params.blobs = auth_blobs; wbc_status = wbcCredentialCache(¶ms, &info, &error); wbcFreeMemory(error); if (!WBC_ERROR_IS_OK(wbc_status)) { return NT_STATUS_WRONG_CREDENTIAL_HANDLE; } for (i=0; i<info->num_blobs; i++) { if (strequal(info->blobs[i].name, "auth_blob")) { wbc_auth_blob = &info->blobs[i].blob; } if (strequal(info->blobs[i].name, "session_key")) { wbc_session_key = &info->blobs[i].blob; } } if ((wbc_auth_blob == NULL) || (wbc_session_key == NULL)) { wbcFreeMemory(info); return NT_STATUS_WRONG_CREDENTIAL_HANDLE; } session_key = data_blob_talloc(mem_ctx, wbc_session_key->data, wbc_session_key->length); if (session_key.length != wbc_session_key->length) { wbcFreeMemory(info); return NT_STATUS_NO_MEMORY; } *out = data_blob_talloc(mem_ctx, wbc_auth_blob->data, wbc_auth_blob->length); if (out->length != wbc_auth_blob->length) { wbcFreeMemory(info); return NT_STATUS_NO_MEMORY; } wbcFreeMemory(info); goto done; } if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) { flags |= CLI_CRED_NTLM2; } if (ntlmssp_state->use_ntlmv2) { flags |= CLI_CRED_NTLMv2_AUTH; } if (ntlmssp_state->use_nt_response) { flags |= CLI_CRED_NTLM_AUTH; } if (lpcfg_client_lanman_auth(gensec_security->settings->lp_ctx)) { flags |= CLI_CRED_LANMAN_AUTH; } nt_status = cli_credentials_get_ntlm_response(gensec_security->credentials, mem_ctx, &flags, challenge_blob, target_info, &lm_response, &nt_response, &lm_session_key, &session_key); if (!NT_STATUS_IS_OK(nt_status)) { return nt_status; } if (!(flags & CLI_CRED_LANMAN_AUTH)) { /* LM Key is still possible, just silly, so we do not * allow it. Fortunetly all LM crypto is off by * default and we require command line options to end * up here */ ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY; } if (!(flags & CLI_CRED_NTLM2)) { /* NTLM2 is incompatible... */ ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_NTLM2; } if ((ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_LM_KEY) && lpcfg_client_lanman_auth(gensec_security->settings->lp_ctx) && lm_session_key.length == 16) { DATA_BLOB new_session_key = data_blob_talloc(mem_ctx, NULL, 16); if (lm_response.length == 24) { SMBsesskeygen_lm_sess_key(lm_session_key.data, lm_response.data, new_session_key.data); } else { static const uint8_t zeros[24]; SMBsesskeygen_lm_sess_key(lm_session_key.data, zeros, new_session_key.data); } session_key = new_session_key; dump_data_pw("LM session key\n", session_key.data, session_key.length); } /* Key exchange encryptes a new client-generated session key with the password-derived key */ if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) { /* Make up a new session key */ uint8_t client_session_key[16]; generate_secret_buffer(client_session_key, sizeof(client_session_key)); /* Encrypt the new session key with the old one */ encrypted_session_key = data_blob_talloc(ntlmssp_state, client_session_key, sizeof(client_session_key)); dump_data_pw("KEY_EXCH session key:\n", encrypted_session_key.data, encrypted_session_key.length); arcfour_crypt(encrypted_session_key.data, session_key.data, encrypted_session_key.length); dump_data_pw("KEY_EXCH session key (enc):\n", encrypted_session_key.data, encrypted_session_key.length); /* Mark the new session key as the 'real' session key */ session_key = data_blob_talloc(mem_ctx, client_session_key, sizeof(client_session_key)); } /* this generates the actual auth packet */ nt_status = msrpc_gen(mem_ctx, out, auth_gen_string, "NTLMSSP", NTLMSSP_AUTH, lm_response.data, lm_response.length, nt_response.data, nt_response.length, domain, user, workstation, encrypted_session_key.data, encrypted_session_key.length, ntlmssp_state->neg_flags, version_blob.data, version_blob.length); if (!NT_STATUS_IS_OK(nt_status)) { talloc_free(mem_ctx); return nt_status; } done: ntlmssp_state->session_key = session_key; talloc_steal(ntlmssp_state, session_key.data); DEBUG(3, ("NTLMSSP: Set final flags:\n")); debug_ntlmssp_flags(ntlmssp_state->neg_flags); talloc_steal(out_mem_ctx, out->data); ntlmssp_state->expected_state = NTLMSSP_DONE; if (gensec_security->want_features & (GENSEC_FEATURE_SIGN|GENSEC_FEATURE_SEAL)) { nt_status = ntlmssp_sign_init(ntlmssp_state); if (!NT_STATUS_IS_OK(nt_status)) { DEBUG(1, ("Could not setup NTLMSSP signing/sealing system (error was: %s)\n", nt_errstr(nt_status))); talloc_free(mem_ctx); return nt_status; } } talloc_free(mem_ctx); return NT_STATUS_OK; }