static ADS_STATUS ads_sasl_gensec_wrap(ADS_STRUCT *ads, uint8_t *buf, uint32_t len) { struct gensec_security *gensec_security = talloc_get_type_abort(ads->ldap.wrap_private_data, struct gensec_security); NTSTATUS nt_status; 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); } if ((ads->ldap.out.size - 4) < wrapped.length) { TALLOC_FREE(frame); return ADS_ERROR_NT(NT_STATUS_INTERNAL_ERROR); } /* 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 + wrapped.length; TALLOC_FREE(frame); return ADS_SUCCESS; }
static ADS_STATUS ads_sasl_ntlmssp_unwrap(ADS_STRUCT *ads) { struct gensec_security *gensec_security = talloc_get_type_abort(ads->ldap.wrap_private_data, struct gensec_security); NTSTATUS nt_status; 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); } 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; }
ADS_STATUS gpo_process_a_gpo(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const struct security_token *token, struct registry_key *root_key, struct GROUP_POLICY_OBJECT *gpo, const char *extension_guid_filter, uint32_t flags) { struct GP_EXT *gp_ext = NULL; int i; DEBUG(10,("gpo_process_a_gpo: processing gpo %s (%s)\n", gpo->name, gpo->display_name)); if (extension_guid_filter) { DEBUGADD(10,("gpo_process_a_gpo: using filter %s (%s)\n", extension_guid_filter, cse_gpo_guid_string_to_name(extension_guid_filter))); } if (!gpo_get_gp_ext_from_gpo(mem_ctx, flags, gpo, &gp_ext)) { return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER); } if (!gp_ext || !gp_ext->num_exts) { if (flags & GPO_INFO_FLAG_VERBOSE) { DEBUG(0,("gpo_process_a_gpo: " "no policies in %s (%s) for this extension\n", gpo->name, gpo->display_name)); } return ADS_SUCCESS; } for (i=0; i<gp_ext->num_exts; i++) { NTSTATUS ntstatus; if (extension_guid_filter && !strequal(extension_guid_filter, gp_ext->extensions_guid[i])) { continue; } ntstatus = gpext_process_extension(ads, mem_ctx, flags, token, root_key, gpo, gp_ext->extensions_guid[i], gp_ext->snapins_guid[i]); if (!NT_STATUS_IS_OK(ntstatus)) { ADS_ERROR_NT(ntstatus); } } 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; 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); } 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; return ADS_SUCCESS; }
/* perform a LDAP/SASL/SPNEGO/KRB5 bind */ static ADS_STATUS ads_sasl_spnego_rawkrb5_bind(ADS_STRUCT *ads, const char *principal) { DATA_BLOB blob = data_blob_null; struct berval cred, *scred = NULL; DATA_BLOB session_key = data_blob_null; int rc; if (ads->ldap.wrap_type > ADS_SASLWRAP_TYPE_PLAIN) { return ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED); } rc = spnego_gen_negTokenTarg(principal, ads->auth.time_offset, &blob, &session_key, 0, &ads->auth.tgs_expire); if (rc) { return ADS_ERROR_KRB5(rc); } /* now send the auth packet and we should be done */ cred.bv_val = (char *)blob.data; cred.bv_len = blob.length; rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred); data_blob_free(&blob); data_blob_free(&session_key); if(scred) ber_bvfree(scred); return ADS_ERROR(rc); }
static ADS_STATUS ads_connect(ADS_STRUCT *ads) { struct libnet_LookupDCs *io; char *url; io = talloc_zero(ads, struct libnet_LookupDCs); /* We are looking for the PDC of the active domain. */ io->in.name_type = NBT_NAME_PDC; io->in.domain_name = lp_workgroup(ads->netctx->lp_ctx); libnet_LookupDCs(ads->netctx, ads, io); url = talloc_asprintf(ads, "ldap://%s", io->out.dcs[0].name); ads->ldbctx = ldb_wrap_connect(ads, ads->netctx->event_ctx, ads->netctx->lp_ctx, url, NULL, ads->netctx->cred, 0); if (ads->ldbctx == NULL) { return ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL); } return ADS_ERROR_NT(NT_STATUS_OK); }
static ADS_STATUS ads_sasl_gssapi_wrap(struct ads_saslwrap *wrap, uint8_t *buf, uint32_t len) { gss_ctx_id_t context_handle = (gss_ctx_id_t)wrap->wrap_private_data; ADS_STATUS status; int gss_rc; uint32_t minor_status; gss_buffer_desc unwrapped, wrapped; int conf_req_flag, conf_state; unwrapped.value = buf; unwrapped.length = len; /* for now request sign and seal */ conf_req_flag = (wrap->wrap_type == ADS_SASLWRAP_TYPE_SEAL); gss_rc = gss_wrap(&minor_status, context_handle, conf_req_flag, GSS_C_QOP_DEFAULT, &unwrapped, &conf_state, &wrapped); status = ADS_ERROR_GSS(gss_rc, minor_status); if (!ADS_ERR_OK(status)) return status; if (conf_req_flag && conf_state == 0) { return ADS_ERROR_NT(NT_STATUS_ACCESS_DENIED); } if ((wrap->out.size - 4) < wrapped.length) { return ADS_ERROR_NT(NT_STATUS_INTERNAL_ERROR); } /* copy the wrapped blob to the right location */ memcpy(wrap->out.buf + 4, wrapped.value, wrapped.length); /* set how many bytes must be written to the underlying socket */ wrap->out.left = 4 + wrapped.length; gss_release_buffer(&minor_status, &wrapped); return ADS_SUCCESS; }
static ADS_STATUS ads_sasl_gssapi_unwrap(struct ads_saslwrap *wrap) { gss_ctx_id_t context_handle = (gss_ctx_id_t)wrap->wrap_private_data; ADS_STATUS status; int gss_rc; uint32_t minor_status; gss_buffer_desc unwrapped, wrapped; int conf_state; wrapped.value = wrap->in.buf + 4; wrapped.length = wrap->in.ofs - 4; gss_rc = gss_unwrap(&minor_status, context_handle, &wrapped, &unwrapped, &conf_state, GSS_C_QOP_DEFAULT); status = ADS_ERROR_GSS(gss_rc, minor_status); if (!ADS_ERR_OK(status)) return status; if (wrap->wrap_type == ADS_SASLWRAP_TYPE_SEAL && conf_state == 0) { return ADS_ERROR_NT(NT_STATUS_ACCESS_DENIED); } if (wrapped.length < unwrapped.length) { return ADS_ERROR_NT(NT_STATUS_INTERNAL_ERROR); } /* copy the wrapped blob to the right location */ memcpy(wrap->in.buf + 4, unwrapped.value, unwrapped.length); /* set how many bytes must be written to the underlying socket */ wrap->in.left = unwrapped.length; wrap->in.ofs = 4; gss_release_buffer(&minor_status, &unwrapped); return ADS_SUCCESS; }
ADS_STATUS gp_get_machine_token(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx, const char *dn, struct security_token **token) { struct security_token *ad_token = NULL; ADS_STATUS status; NTSTATUS ntstatus; #ifndef HAVE_ADS return ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED); #endif status = ads_get_sid_token(ads, mem_ctx, dn, &ad_token); if (!ADS_ERR_OK(status)) { return status; } ntstatus = merge_nt_token(mem_ctx, ad_token, get_system_token(), token); if (!NT_STATUS_IS_OK(ntstatus)) { return ADS_ERROR_NT(ntstatus); } return ADS_SUCCESS; }
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; NTSTATUS nt_status; DATA_BLOB sig; TALLOC_CTX *frame; uint8 *dptr = ads->ldap.out.buf + (4 + NTLMSSP_SIG_SIZE); frame = talloc_stackframe(); /* 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, frame, dptr, len, dptr, len, &sig); } else { nt_status = ntlmssp_sign_packet(ntlmssp_state, frame, dptr, len, dptr, len, &sig); } 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); TALLOC_FREE(frame); /* set how many bytes must be written to the underlying socket */ ads->ldap.out.left = 4 + NTLMSSP_SIG_SIZE + len; return ADS_SUCCESS; }
/** * Set the machine account password * @param ads connection to ads server * @param hostname machine whose password is being set * @param password new password * @return status of password change **/ ADS_STATUS ads_set_machine_password(ADS_STRUCT *ads, const char *machine_account, const char *password) { ADS_STATUS status; char *principal = NULL; /* we need to use the '$' form of the name here (the machine account name), as otherwise the server might end up setting the password for a user instead */ if (asprintf(&principal, "%s@%s", machine_account, ads->config.realm) < 0) { return ADS_ERROR_NT(NT_STATUS_NO_MEMORY); } status = ads_krb5_set_password(ads->auth.kdc_server, principal, password, ads->auth.time_offset); SAFE_FREE(principal); return status; }
ADS_STATUS ads_search_retry_extended_dn_ranged(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *dn, const char **attrs, enum ads_extended_dn_flags flags, char ***strings, size_t *num_strings) { ads_control args; args.control = ADS_EXTENDED_DN_OID; args.val = flags; args.critical = True; /* we can only range process one attribute */ if (!attrs || !attrs[0] || attrs[1]) { return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER); } return ads_ranged_search(ads, mem_ctx, LDAP_SCOPE_BASE, dn, "(objectclass=*)", &args, attrs[0], strings, num_strings); }
/* perform a LDAP/SASL/SPNEGO/NTLMSSP bind (just how many layers can we fit on one socket??) */ static ADS_STATUS ads_sasl_spnego_ntlmssp_bind(ADS_STRUCT *ads) { DATA_BLOB msg1 = data_blob_null; DATA_BLOB blob = data_blob_null; DATA_BLOB blob_in = data_blob_null; DATA_BLOB blob_out = data_blob_null; struct berval cred, *scred = NULL; int rc; NTSTATUS nt_status; ADS_STATUS status; int turn = 1; uint32 features = 0; struct ntlmssp_state *ntlmssp_state; if (!NT_STATUS_IS_OK(nt_status = ntlmssp_client_start(&ntlmssp_state))) { 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))) { return ADS_ERROR_NT(nt_status); } if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_domain(ntlmssp_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))) { return ADS_ERROR_NT(nt_status); } switch (ads->ldap.wrap_type) { case ADS_SASLWRAP_TYPE_SEAL: features = NTLMSSP_FEATURE_SIGN | NTLMSSP_FEATURE_SEAL; break; case ADS_SASLWRAP_TYPE_SIGN: if (ads->auth.flags & ADS_AUTH_SASL_FORCE) { features = NTLMSSP_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; ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SEAL; } break; case ADS_SASLWRAP_TYPE_PLAIN: break; } ntlmssp_want_feature(ntlmssp_state, features); blob_in = data_blob_null; do { nt_status = ntlmssp_update(ntlmssp_state, 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) { /* and wrap it in a SPNEGO wrapper */ msg1 = gen_negTokenInit(OID_NTLMSSP, blob_out); } else { /* wrap it in SPNEGO */ msg1 = spnego_gen_auth(blob_out); } data_blob_free(&blob_out); cred.bv_val = (char *)msg1.data; cred.bv_len = msg1.length; scred = NULL; rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred); data_blob_free(&msg1); if ((rc != LDAP_SASL_BIND_IN_PROGRESS) && (rc != 0)) { if (scred) { ber_bvfree(scred); } ntlmssp_end(&ntlmssp_state); return ADS_ERROR(rc); } if (scred) { blob = data_blob(scred->bv_val, scred->bv_len); ber_bvfree(scred); } else { blob = data_blob_null; } } else { ntlmssp_end(&ntlmssp_state); data_blob_free(&blob_out); return ADS_ERROR_NT(nt_status); } if ((turn == 1) && (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, &tmp_blob)) { ntlmssp_end(&ntlmssp_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, &blob_in)) { ntlmssp_end(&ntlmssp_state); data_blob_free(&blob); DEBUG(3,("Failed to parse auth response\n")); return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER); } } data_blob_free(&blob); data_blob_free(&blob_out); 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; 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); if (!ADS_ERR_OK(status)) { DEBUG(0, ("ads_setup_sasl_wrapping() failed: %s\n", ads_errstr(status))); ntlmssp_end(&ntlmssp_state); return status; } } else { ntlmssp_end(&ntlmssp_state); } return ADS_ERROR(rc); }
/* perform a LDAP/SASL/SPNEGO/{NTLMSSP,KRB5} bind (just how many layers can we fit on one socket??) */ static ADS_STATUS ads_sasl_spnego_gensec_bind(ADS_STRUCT *ads, const char *sasl, enum credentials_use_kerberos krb5_state, const char *target_service, const char *target_hostname, const DATA_BLOB server_blob) { DATA_BLOB blob_in = data_blob_null; DATA_BLOB blob_out = data_blob_null; int rc; NTSTATUS nt_status; ADS_STATUS status; struct auth_generic_state *auth_generic_state; bool use_spnego_principal = lp_client_use_spnego_principal(); const char *sasl_list[] = { sasl, NULL }; nt_status = auth_generic_client_prepare(NULL, &auth_generic_state); if (!NT_STATUS_IS_OK(nt_status)) { return ADS_ERROR_NT(nt_status); } 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 = auth_generic_set_domain(auth_generic_state, ads->auth.realm))) { return ADS_ERROR_NT(nt_status); } if (!NT_STATUS_IS_OK(nt_status = auth_generic_set_password(auth_generic_state, ads->auth.password))) { return ADS_ERROR_NT(nt_status); } if (server_blob.length == 0) { use_spnego_principal = false; } if (krb5_state == CRED_DONT_USE_KERBEROS) { use_spnego_principal = false; } cli_credentials_set_kerberos_state(auth_generic_state->credentials, krb5_state); if (target_service != NULL) { nt_status = gensec_set_target_service( auth_generic_state->gensec_security, target_service); if (!NT_STATUS_IS_OK(nt_status)) { return ADS_ERROR_NT(nt_status); } } if (target_hostname != NULL) { nt_status = gensec_set_target_hostname( auth_generic_state->gensec_security, target_hostname); if (!NT_STATUS_IS_OK(nt_status)) { return ADS_ERROR_NT(nt_status); } } if (target_service != NULL && target_hostname != NULL) { use_spnego_principal = false; } switch (ads->ldap.wrap_type) { case ADS_SASLWRAP_TYPE_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) { gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_SIGN); } else { /* * windows servers are broken with sign only, * so we let the NTLMSSP backend to seal here, * via GENSEC_FEATURE_LDAP_STYLE. */ gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_SIGN); gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_LDAP_STYLE); } break; case ADS_SASLWRAP_TYPE_PLAIN: break; } nt_status = auth_generic_client_start_by_sasl(auth_generic_state, sasl_list); if (!NT_STATUS_IS_OK(nt_status)) { return ADS_ERROR_NT(nt_status); } rc = LDAP_SASL_BIND_IN_PROGRESS; nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED; if (use_spnego_principal) { blob_in = data_blob_dup_talloc(talloc_tos(), server_blob); if (blob_in.length == 0) { TALLOC_FREE(auth_generic_state); return ADS_ERROR_NT(NT_STATUS_NO_MEMORY); } } else { blob_in = data_blob_null; } blob_out = data_blob_null; while (true) { struct berval cred, *scred = NULL; nt_status = gensec_update(auth_generic_state->gensec_security, talloc_tos(), 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)) { TALLOC_FREE(auth_generic_state); data_blob_free(&blob_out); return ADS_ERROR_NT(nt_status); } if (NT_STATUS_IS_OK(nt_status) && rc == 0 && blob_out.length == 0) { break; } cred.bv_val = (char *)blob_out.data; cred.bv_len = blob_out.length; scred = NULL; rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, sasl, &cred, NULL, NULL, &scred); data_blob_free(&blob_out); if ((rc != LDAP_SASL_BIND_IN_PROGRESS) && (rc != 0)) { if (scred) { ber_bvfree(scred); } TALLOC_FREE(auth_generic_state); return ADS_ERROR(rc); } if (scred) { blob_in = data_blob_talloc(talloc_tos(), scred->bv_val, scred->bv_len); if (blob_in.length != scred->bv_len) { ber_bvfree(scred); TALLOC_FREE(auth_generic_state); return ADS_ERROR_NT(NT_STATUS_NO_MEMORY); } ber_bvfree(scred); } else { blob_in = data_blob_null; } if (NT_STATUS_IS_OK(nt_status) && rc == 0 && blob_in.length == 0) { break; } } data_blob_free(&blob_in); data_blob_free(&blob_out); if (ads->ldap.wrap_type > ADS_SASLWRAP_TYPE_PLAIN) { size_t max_wrapped = gensec_max_wrapped_size(auth_generic_state->gensec_security); ads->ldap.out.max_unwrapped = gensec_max_input_size(auth_generic_state->gensec_security); ads->ldap.out.sig_size = max_wrapped - ads->ldap.out.max_unwrapped; ads->ldap.in.min_wrapped = ads->ldap.out.sig_size; ads->ldap.in.max_wrapped = max_wrapped; status = ads_setup_sasl_wrapping(ads, &ads_sasl_gensec_ops, auth_generic_state->gensec_security); if (!ADS_ERR_OK(status)) { DEBUG(0, ("ads_setup_sasl_wrapping() failed: %s\n", ads_errstr(status))); TALLOC_FREE(auth_generic_state); return status; } /* 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); }
ADS_STATUS cell_do_search(struct likewise_cell *c, const char *search_base, int scope, const char *expr, const char **attrs, LDAPMessage ** msg) { int search_count = 0; ADS_STATUS status; NTSTATUS nt_status; /* check for a NULL connection */ if (!c->conn) { nt_status = cell_connect(c); if (!NT_STATUS_IS_OK(nt_status)) { status = ADS_ERROR_NT(nt_status); return status; } } DEBUG(10, ("cell_do_search: Base = %s, Filter = %s, Scope = %d, GC = %s\n", search_base, expr, scope, c->conn->server.gc ? "yes" : "no")); /* we try multiple times in case the ADS_STRUCT is bad and we need to reconnect */ while (search_count < MAX_SEARCH_COUNT) { *msg = NULL; status = ads_do_search(c->conn, search_base, scope, expr, attrs, msg); if (ADS_ERR_OK(status)) { if (DEBUGLEVEL >= 10) { LDAPMessage *e = NULL; int n = ads_count_replies(c->conn, *msg); DEBUG(10,("cell_do_search: Located %d entries\n", n)); for (e=ads_first_entry(c->conn, *msg); e!=NULL; e = ads_next_entry(c->conn, e)) { char *dn = ads_get_dn(c->conn, talloc_tos(), e); DEBUGADD(10,(" dn: %s\n", dn ? dn : "<NULL>")); TALLOC_FREE(dn); } } return status; } DEBUG(5, ("cell_do_search: search[%d] failed (%s)\n", search_count, ads_errstr(status))); search_count++; /* Houston, we have a problem */ if (status.error_type == ENUM_ADS_ERROR_LDAP) { switch (status.err.rc) { case LDAP_TIMELIMIT_EXCEEDED: case LDAP_TIMEOUT: case -1: /* we get this error if we cannot contact the LDAP server */ nt_status = cell_connect(c); if (!NT_STATUS_IS_OK(nt_status)) { status = ADS_ERROR_NT(nt_status); return status; } break; default: /* we're all done here */ return status; } } } DEBUG(5, ("cell_do_search: exceeded maximum search count!\n")); return ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL); }
ADS_STATUS ads_sid_to_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const DOM_SID *sid, char **dn) { ADS_STATUS rc; LDAPMessage *msg = NULL; LDAPMessage *entry = NULL; char *ldap_exp; char *sidstr = NULL; int count; char *dn2 = NULL; const char *attr[] = { "dn", NULL }; if (!(sidstr = sid_binstring(sid))) { DEBUG(1,("ads_sid_to_dn: sid_binstring failed!\n")); rc = ADS_ERROR_NT(NT_STATUS_NO_MEMORY); goto done; } if(!(ldap_exp = talloc_asprintf(mem_ctx, "(objectSid=%s)", sidstr))) { DEBUG(1,("ads_sid_to_dn: talloc_asprintf failed!\n")); rc = ADS_ERROR_NT(NT_STATUS_NO_MEMORY); goto done; } rc = ads_search_retry(ads, (void **)&msg, ldap_exp, attr); if (!ADS_ERR_OK(rc)) { DEBUG(1,("ads_sid_to_dn ads_search: %s\n", ads_errstr(rc))); goto done; } if ((count = ads_count_replies(ads, msg)) != 1) { fstring sid_string; DEBUG(1,("ads_sid_to_dn (sid=%s): Not found (count=%d)\n", sid_to_string(sid_string, sid), count)); rc = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL); goto done; } entry = ads_first_entry(ads, msg); dn2 = ads_get_dn(ads, entry); if (!dn2) { rc = ADS_ERROR_NT(NT_STATUS_NO_MEMORY); goto done; } *dn = talloc_strdup(mem_ctx, dn2); if (!*dn) { ads_memfree(ads, dn2); rc = ADS_ERROR_NT(NT_STATUS_NO_MEMORY); goto done; } rc = ADS_ERROR_NT(NT_STATUS_OK); DEBUG(3,("ads sid_to_dn mapped %s\n", dn2)); SAFE_FREE(dn2); done: if (msg) ads_msgfree(ads, msg); if (dn2) ads_memfree(ads, dn2); SAFE_FREE(sidstr); return rc; }
/* perform a LDAP/SASL/SPNEGO/NTLMSSP bind (just how many layers can we fit on one socket??) */ static ADS_STATUS ads_sasl_spnego_ntlmssp_bind(ADS_STRUCT *ads) { DATA_BLOB msg1 = data_blob(NULL, 0); DATA_BLOB blob = data_blob(NULL, 0); DATA_BLOB blob_in = data_blob(NULL, 0); DATA_BLOB blob_out = data_blob(NULL, 0); struct berval cred, *scred = NULL; int rc; NTSTATUS nt_status; int turn = 1; struct ntlmssp_state *ntlmssp_state; if (!NT_STATUS_IS_OK(nt_status = ntlmssp_client_start(&ntlmssp_state))) { 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))) { return ADS_ERROR_NT(nt_status); } if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_domain(ntlmssp_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))) { return ADS_ERROR_NT(nt_status); } blob_in = data_blob(NULL, 0); do { nt_status = ntlmssp_update(ntlmssp_state, 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) { /* and wrap it in a SPNEGO wrapper */ msg1 = gen_negTokenInit(OID_NTLMSSP, blob_out); } else { /* wrap it in SPNEGO */ msg1 = spnego_gen_auth(blob_out); } data_blob_free(&blob_out); cred.bv_val = (char *)msg1.data; cred.bv_len = msg1.length; scred = NULL; rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred); data_blob_free(&msg1); if ((rc != LDAP_SASL_BIND_IN_PROGRESS) && (rc != 0)) { if (scred) { ber_bvfree(scred); } ntlmssp_end(&ntlmssp_state); return ADS_ERROR(rc); } if (scred) { blob = data_blob(scred->bv_val, scred->bv_len); ber_bvfree(scred); } else { blob = data_blob(NULL, 0); } } else { ntlmssp_end(&ntlmssp_state); data_blob_free(&blob_out); return ADS_ERROR_NT(nt_status); } if ((turn == 1) && (rc == LDAP_SASL_BIND_IN_PROGRESS)) { DATA_BLOB tmp_blob = data_blob(NULL, 0); /* the server might give us back two challenges */ if (!spnego_parse_challenge(blob, &blob_in, &tmp_blob)) { ntlmssp_end(&ntlmssp_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, &blob_in)) { ntlmssp_end(&ntlmssp_state); data_blob_free(&blob); DEBUG(3,("Failed to parse auth response\n")); return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER); } } data_blob_free(&blob); data_blob_free(&blob_out); 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 */ ntlmssp_end(&ntlmssp_state); return ADS_ERROR(rc); }
/* perform a LDAP/SASL/SPNEGO/{NTLMSSP,KRB5} bind (just how many layers can we fit on one socket??) */ static ADS_STATUS ads_sasl_spnego_gensec_bind(ADS_STRUCT *ads, const char *sasl, enum credentials_use_kerberos krb5_state, const char *target_service, const char *target_hostname, const DATA_BLOB server_blob) { DATA_BLOB blob_in = data_blob_null; DATA_BLOB blob_out = data_blob_null; int rc; NTSTATUS nt_status; ADS_STATUS status; struct auth_generic_state *auth_generic_state; bool use_spnego_principal = lp_client_use_spnego_principal(); const char *sasl_list[] = { sasl, NULL }; NTTIME end_nt_time; struct ads_saslwrap *wrap = &ads->ldap_wrap_data; nt_status = auth_generic_client_prepare(NULL, &auth_generic_state); if (!NT_STATUS_IS_OK(nt_status)) { return ADS_ERROR_NT(nt_status); } 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 = auth_generic_set_domain(auth_generic_state, ads->auth.realm))) { return ADS_ERROR_NT(nt_status); } if (!NT_STATUS_IS_OK(nt_status = auth_generic_set_password(auth_generic_state, ads->auth.password))) { return ADS_ERROR_NT(nt_status); } if (server_blob.length == 0) { use_spnego_principal = false; } if (krb5_state == CRED_DONT_USE_KERBEROS) { use_spnego_principal = false; } cli_credentials_set_kerberos_state(auth_generic_state->credentials, krb5_state); if (target_service != NULL) { nt_status = gensec_set_target_service( auth_generic_state->gensec_security, target_service); if (!NT_STATUS_IS_OK(nt_status)) { return ADS_ERROR_NT(nt_status); } } if (target_hostname != NULL) { nt_status = gensec_set_target_hostname( auth_generic_state->gensec_security, target_hostname); if (!NT_STATUS_IS_OK(nt_status)) { return ADS_ERROR_NT(nt_status); } } if (target_service != NULL && target_hostname != NULL) { use_spnego_principal = false; } switch (wrap->wrap_type) { case ADS_SASLWRAP_TYPE_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) { gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_SIGN); } else { /* * windows servers are broken with sign only, * so we let the NTLMSSP backend to seal here, * via GENSEC_FEATURE_LDAP_STYLE. */ gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_SIGN); gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_LDAP_STYLE); } break; case ADS_SASLWRAP_TYPE_PLAIN: break; } nt_status = auth_generic_client_start_by_sasl(auth_generic_state, sasl_list); if (!NT_STATUS_IS_OK(nt_status)) { return ADS_ERROR_NT(nt_status); } rc = LDAP_SASL_BIND_IN_PROGRESS; nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED; if (use_spnego_principal) { blob_in = data_blob_dup_talloc(talloc_tos(), server_blob); if (blob_in.length == 0) { TALLOC_FREE(auth_generic_state); return ADS_ERROR_NT(NT_STATUS_NO_MEMORY); } } else { blob_in = data_blob_null; } blob_out = data_blob_null; while (true) { struct berval cred, *scred = NULL; nt_status = gensec_update(auth_generic_state->gensec_security, talloc_tos(), 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)) { TALLOC_FREE(auth_generic_state); data_blob_free(&blob_out); return ADS_ERROR_NT(nt_status); } if (NT_STATUS_IS_OK(nt_status) && rc == 0 && blob_out.length == 0) { break; } cred.bv_val = (char *)blob_out.data; cred.bv_len = blob_out.length; scred = NULL; rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, sasl, &cred, NULL, NULL, &scred); data_blob_free(&blob_out); if ((rc != LDAP_SASL_BIND_IN_PROGRESS) && (rc != 0)) { if (scred) { ber_bvfree(scred); } TALLOC_FREE(auth_generic_state); return ADS_ERROR(rc); } if (scred) { blob_in = data_blob_talloc(talloc_tos(), scred->bv_val, scred->bv_len); if (blob_in.length != scred->bv_len) { ber_bvfree(scred); TALLOC_FREE(auth_generic_state); return ADS_ERROR_NT(NT_STATUS_NO_MEMORY); } ber_bvfree(scred); } else { blob_in = data_blob_null; } if (NT_STATUS_IS_OK(nt_status) && rc == 0 && blob_in.length == 0) { break; } } data_blob_free(&blob_in); data_blob_free(&blob_out); if (wrap->wrap_type >= ADS_SASLWRAP_TYPE_SEAL) { bool ok; ok = gensec_have_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_SEAL); if (!ok) { DEBUG(0,("The gensec feature sealing request, but unavailable\n")); TALLOC_FREE(auth_generic_state); return ADS_ERROR_NT(NT_STATUS_INVALID_NETWORK_RESPONSE); } ok = gensec_have_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_SIGN); if (!ok) { DEBUG(0,("The gensec feature signing request, but unavailable\n")); TALLOC_FREE(auth_generic_state); return ADS_ERROR_NT(NT_STATUS_INVALID_NETWORK_RESPONSE); } } else if (wrap->wrap_type >= ADS_SASLWRAP_TYPE_SIGN) { bool ok; ok = gensec_have_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_SIGN); if (!ok) { DEBUG(0,("The gensec feature signing request, but unavailable\n")); TALLOC_FREE(auth_generic_state); return ADS_ERROR_NT(NT_STATUS_INVALID_NETWORK_RESPONSE); } } ads->auth.tgs_expire = LONG_MAX; end_nt_time = gensec_expire_time(auth_generic_state->gensec_security); if (end_nt_time != GENSEC_EXPIRE_TIME_INFINITY) { struct timeval tv; nttime_to_timeval(&tv, end_nt_time); ads->auth.tgs_expire = tv.tv_sec; } if (wrap->wrap_type > ADS_SASLWRAP_TYPE_PLAIN) { size_t max_wrapped = gensec_max_wrapped_size(auth_generic_state->gensec_security); wrap->out.max_unwrapped = gensec_max_input_size(auth_generic_state->gensec_security); wrap->out.sig_size = max_wrapped - wrap->out.max_unwrapped; /* * Note that we have to truncate this to 0x2C * (taken from a capture with LDAP unbind), as the * signature size is not constant for Kerberos with * arcfour-hmac-md5. */ wrap->in.min_wrapped = MIN(wrap->out.sig_size, 0x2C); wrap->in.max_wrapped = ADS_SASL_WRAPPING_IN_MAX_WRAPPED; status = ads_setup_sasl_wrapping(wrap, ads->ldap.ld, &ads_sasl_gensec_ops, auth_generic_state->gensec_security); if (!ADS_ERR_OK(status)) { DEBUG(0, ("ads_setup_sasl_wrapping() failed: %s\n", ads_errstr(status))); TALLOC_FREE(auth_generic_state); return status; } /* 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); }
static ADS_STATUS ads_krb5_chg_password(const char *kdc_host, const char *principal, const char *oldpw, const char *newpw, int time_offset) { ADS_STATUS aret; krb5_error_code ret; krb5_context context = NULL; krb5_principal princ; krb5_get_init_creds_opt opts; krb5_creds creds; char *chpw_princ = NULL, *password; char *realm = NULL; int result_code; krb5_data result_code_string = { 0 }; krb5_data result_string = { 0 }; smb_krb5_addresses *addr = NULL; initialize_krb5_error_table(); ret = krb5_init_context(&context); if (ret) { DEBUG(1,("Failed to init krb5 context (%s)\n", error_message(ret))); return ADS_ERROR_KRB5(ret); } if ((ret = smb_krb5_parse_name(context, principal, &princ))) { krb5_free_context(context); DEBUG(1,("Failed to parse %s (%s)\n", principal, error_message(ret))); return ADS_ERROR_KRB5(ret); } krb5_get_init_creds_opt_init(&opts); krb5_get_init_creds_opt_set_tkt_life(&opts, 5*60); krb5_get_init_creds_opt_set_renew_life(&opts, 0); krb5_get_init_creds_opt_set_forwardable(&opts, 0); krb5_get_init_creds_opt_set_proxiable(&opts, 0); /* note that heimdal will fill in the local addresses if the addresses * in the creds_init_opt are all empty and then later fail with invalid * address, sending our local netbios krb5 address - just like windows * - avoids this - gd */ ret = smb_krb5_gen_netbios_krb5_address(&addr, lp_netbios_name()); if (ret) { krb5_free_principal(context, princ); krb5_free_context(context); return ADS_ERROR_KRB5(ret); } krb5_get_init_creds_opt_set_address_list(&opts, addr->addrs); realm = smb_krb5_principal_get_realm(context, princ); /* We have to obtain an INITIAL changepw ticket for changing password */ if (asprintf(&chpw_princ, "kadmin/changepw@%s", realm) == -1) { krb5_free_context(context); free(realm); DEBUG(1,("ads_krb5_chg_password: asprintf fail\n")); return ADS_ERROR_NT(NT_STATUS_NO_MEMORY); } free(realm); password = SMB_STRDUP(oldpw); ret = krb5_get_init_creds_password(context, &creds, princ, password, kerb_prompter, NULL, 0, chpw_princ, &opts); SAFE_FREE(chpw_princ); SAFE_FREE(password); if (ret) { if (ret == KRB5KRB_AP_ERR_BAD_INTEGRITY) DEBUG(1,("Password incorrect while getting initial ticket")); else DEBUG(1,("krb5_get_init_creds_password failed (%s)\n", error_message(ret))); krb5_free_principal(context, princ); krb5_free_context(context); return ADS_ERROR_KRB5(ret); } ret = krb5_change_password(context, &creds, discard_const_p(char, newpw), &result_code, &result_code_string, &result_string); if (ret) { DEBUG(1, ("krb5_change_password failed (%s)\n", error_message(ret))); aret = ADS_ERROR_KRB5(ret); goto done; } if (result_code != KRB5_KPASSWD_SUCCESS) { ret = kpasswd_err_to_krb5_err(result_code); DEBUG(1, ("krb5_change_password failed (%s)\n", error_message(ret))); aret = ADS_ERROR_KRB5(ret); goto done; } aret = ADS_SUCCESS; done: kerberos_free_data_contents(context, &result_code_string); kerberos_free_data_contents(context, &result_string); krb5_free_principal(context, princ); krb5_free_context(context); return aret; }
ADS_STATUS gpo_process_gpo_list(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const struct security_token *token, struct GROUP_POLICY_OBJECT *gpo_list, const char *extensions_guid_filter, uint32_t flags) { ADS_STATUS status = ADS_SUCCESS; struct gp_extension *gp_ext_list = NULL; struct gp_extension *gp_ext = NULL; struct registry_key *root_key = NULL; struct gp_registry_context *reg_ctx = NULL; #if 0 WERROR werr; #endif status = ADS_ERROR_NT(init_gp_extensions(mem_ctx)); if (!ADS_ERR_OK(status)) { return status; } gp_ext_list = get_gp_extension_list(); if (!gp_ext_list) { return ADS_ERROR_NT(NT_STATUS_DLL_INIT_FAILED); } /* FIXME Needs to be replaced with new patchfile_preg calls */ #if 0 /* get the key here */ if (flags & GPO_LIST_FLAG_MACHINE) { werr = gp_init_reg_ctx(mem_ctx, KEY_HKLM, REG_KEY_WRITE, get_system_token(), ®_ctx); } else { werr = gp_init_reg_ctx(mem_ctx, KEY_HKCU, REG_KEY_WRITE, token, ®_ctx); } if (!W_ERROR_IS_OK(werr)) { talloc_free(reg_ctx); return ADS_ERROR_NT(werror_to_ntstatus(werr)); } #endif root_key = reg_ctx->curr_key; for (gp_ext = gp_ext_list; gp_ext; gp_ext = gp_ext->next) { const char *guid_str = NULL; guid_str = GUID_string(mem_ctx, gp_ext->guid); if (!guid_str) { status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY); goto done; } if (extensions_guid_filter && (!strequal(guid_str, extensions_guid_filter))) { continue; } DEBUG(0,("-------------------------------------------------\n")); DEBUG(0,("gpo_process_gpo_list: processing ext: %s {%s}\n", gp_ext->name, guid_str)); status = gpo_process_gpo_list_by_ext(ads, mem_ctx, token, root_key, gpo_list, guid_str, flags); if (!ADS_ERR_OK(status)) { goto done; } } done: talloc_free(reg_ctx); talloc_free(root_key); free_gp_extensions(); return status; }
ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods, const char *name, const char *val) { return ADS_ERROR_NT(NT_STATUS_NOT_IMPLEMENTED); }
/* perform a LDAP/SASL/SPNEGO/GSSKRB5 bind */ static ADS_STATUS ads_sasl_spnego_gsskrb5_bind(ADS_STRUCT *ads, const gss_name_t serv_name) { ADS_STATUS status; bool ok; 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") }; 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}; gss_ctx_id_t context_handle = GSS_C_NO_CONTEXT; gss_buffer_desc input_token, output_token; uint32 req_flags, ret_flags; uint32 req_tmp, ret_tmp; DATA_BLOB unwrapped; DATA_BLOB wrapped; struct berval cred, *scred = NULL; input_token.value = NULL; input_token.length = 0; req_flags = GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG; switch (ads->ldap.wrap_type) { case ADS_SASLWRAP_TYPE_SEAL: req_flags |= GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG; break; case ADS_SASLWRAP_TYPE_SIGN: req_flags |= GSS_C_INTEG_FLAG; break; case ADS_SASLWRAP_TYPE_PLAIN: break; } /* Note: here we explicit ask for the krb5 mech_type */ gss_rc = gss_init_sec_context(&minor_status, GSS_C_NO_CREDENTIAL, &context_handle, serv_name, mech_type, req_flags, 0, NULL, &input_token, &actual_mech_type, &output_token, &ret_flags, NULL); if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) { status = ADS_ERROR_GSS(gss_rc, minor_status); goto failed; } /* * As some gssapi krb5 mech implementations * automaticly add GSS_C_INTEG_FLAG and GSS_C_CONF_FLAG * to req_flags internaly, it's not possible to * use plain or signing only connection via * the gssapi interface. * * Because of this we need to check it the ret_flags * has more flags as req_flags and correct the value * of ads->ldap.wrap_type. * * I ads->auth.flags has ADS_AUTH_SASL_FORCE * we need to give an error. */ req_tmp = req_flags & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG); ret_tmp = ret_flags & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG); if (req_tmp == ret_tmp) { /* everythings fine... */ } else if (req_flags & GSS_C_CONF_FLAG) { /* * here we wanted sealing but didn't got it * from the gssapi library */ status = ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED); goto failed; } else if ((req_flags & GSS_C_INTEG_FLAG) && !(ret_flags & GSS_C_INTEG_FLAG)) { /* * here we wanted siging but didn't got it * from the gssapi library */ status = ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED); goto failed; } else if (ret_flags & GSS_C_CONF_FLAG) { /* * here we didn't want sealing * but the gssapi library forces it * so correct the needed wrap_type if * the caller didn't forced siging only */ if (ads->auth.flags & ADS_AUTH_SASL_FORCE) { status = ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED); goto failed; } ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SEAL; req_flags = ret_flags; } else if (ret_flags & GSS_C_INTEG_FLAG) { /* * here we didn't want signing * but the gssapi library forces it * so correct the needed wrap_type if * the caller didn't forced plain */ if (ads->auth.flags & ADS_AUTH_SASL_FORCE) { status = ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED); goto failed; } ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SIGN; req_flags = ret_flags; } else { /* * This could (should?) not happen */ status = ADS_ERROR_NT(NT_STATUS_INTERNAL_ERROR); goto failed; } /* and wrap that in a shiny SPNEGO wrapper */ unwrapped = data_blob_const(output_token.value, output_token.length); wrapped = gen_negTokenTarg(spnego_mechs, unwrapped); gss_release_buffer(&minor_status, &output_token); if (unwrapped.length > wrapped.length) { status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY); goto failed; } cred.bv_val = (char *)wrapped.data; cred.bv_len = wrapped.length; rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred); data_blob_free(&wrapped); if (rc != LDAP_SUCCESS) { status = ADS_ERROR(rc); goto failed; } if (scred) { wrapped = data_blob_const(scred->bv_val, scred->bv_len); } else { wrapped = data_blob_null; } ok = spnego_parse_auth_response(wrapped, NT_STATUS_OK, OID_KERBEROS5_OLD, &unwrapped); if (scred) ber_bvfree(scred); if (!ok) { status = ADS_ERROR_NT(NT_STATUS_INVALID_NETWORK_RESPONSE); goto failed; } input_token.value = unwrapped.data; input_token.length = unwrapped.length; /* * As we asked for mutal authentication * we need to pass the servers response * to gssapi */ gss_rc = gss_init_sec_context(&minor_status, GSS_C_NO_CREDENTIAL, &context_handle, serv_name, mech_type, req_flags, 0, NULL, &input_token, &actual_mech_type, &output_token, &ret_flags, NULL); data_blob_free(&unwrapped); if (gss_rc) { status = ADS_ERROR_GSS(gss_rc, minor_status); goto failed; } gss_release_buffer(&minor_status, &output_token); /* * If we the sign and seal options * doesn't match after getting the response * from the server, we don't want to use the connection */ req_tmp = req_flags & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG); ret_tmp = ret_flags & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG); if (req_tmp != ret_tmp) { /* everythings fine... */ status = ADS_ERROR_NT(NT_STATUS_INVALID_NETWORK_RESPONSE); goto failed; } if (ads->ldap.wrap_type > ADS_SASLWRAP_TYPE_PLAIN) { uint32 max_msg_size = ADS_SASL_WRAPPING_OUT_MAX_WRAPPED; gss_rc = gss_wrap_size_limit(&minor_status, context_handle, (ads->ldap.wrap_type == ADS_SASLWRAP_TYPE_SEAL), GSS_C_QOP_DEFAULT, max_msg_size, &ads->ldap.out.max_unwrapped); if (gss_rc) { status = ADS_ERROR_GSS(gss_rc, minor_status); goto failed; } ads->ldap.out.sig_size = max_msg_size - ads->ldap.out.max_unwrapped; ads->ldap.in.min_wrapped = 0x2C; /* taken from a capture with LDAP unbind */ ads->ldap.in.max_wrapped = max_msg_size; status = ads_setup_sasl_wrapping(ads, &ads_sasl_gssapi_ops, context_handle); if (!ADS_ERR_OK(status)) { DEBUG(0, ("ads_setup_sasl_wrapping() failed: %s\n", ads_errstr(status))); goto failed; } /* make sure we don't free context_handle */ context_handle = GSS_C_NO_CONTEXT; } status = ADS_SUCCESS; failed: if (context_handle != GSS_C_NO_CONTEXT) gss_delete_sec_context(&minor_status, &context_handle, GSS_C_NO_BUFFER); return status; }
/** * @brief Establish a connection to a DC * * @param[out] adsp ADS_STRUCT that will be created * @param[in] target_realm Realm of domain to connect to * @param[in] target_dom_name 'workgroup' name of domain to connect to * @param[in] ldap_server DNS name of server to connect to * @param[in] password Our machine acount secret * @param[in] auth_realm Realm of local domain for creating krb token * @param[in] renewable Renewable ticket time * * @return ADS_STATUS */ static ADS_STATUS ads_cached_connection_connect(ADS_STRUCT **adsp, const char *target_realm, const char *target_dom_name, const char *ldap_server, char *password, char *auth_realm, time_t renewable) { ADS_STRUCT *ads; ADS_STATUS status; struct sockaddr_storage dc_ss; fstring dc_name; if (auth_realm == NULL) { return ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL); } /* we don't want this to affect the users ccache */ setenv("KRB5CCNAME", WINBIND_CCACHE_NAME, 1); ads = ads_init(target_realm, target_dom_name, ldap_server); if (!ads) { DEBUG(1,("ads_init for domain %s failed\n", target_dom_name)); return ADS_ERROR(LDAP_NO_MEMORY); } SAFE_FREE(ads->auth.password); SAFE_FREE(ads->auth.realm); ads->auth.renewable = renewable; ads->auth.password = password; ads->auth.flags |= ADS_AUTH_ALLOW_NTLMSSP; ads->auth.realm = SMB_STRDUP(auth_realm); if (!strupper_m(ads->auth.realm)) { ads_destroy(&ads); return ADS_ERROR_NT(NT_STATUS_INTERNAL_ERROR); } /* Setup the server affinity cache. We don't reaally care about the name. Just setup affinity and the KRB5_CONFIG file. */ get_dc_name(ads->server.workgroup, ads->server.realm, dc_name, &dc_ss); status = ads_connect(ads); if (!ADS_ERR_OK(status)) { DEBUG(1,("ads_connect for domain %s failed: %s\n", target_dom_name, ads_errstr(status))); ads_destroy(&ads); return status; } /* set the flag that says we don't own the memory even though we do so that ads_destroy() won't destroy the structure we pass back by reference */ ads->is_mine = False; *adsp = ads; return status; }
ADS_STATUS ads_msgfree(ADS_STRUCT *ads, LDAPMessage *res) { talloc_free(res); return ADS_ERROR_NT(NT_STATUS_OK); }
/* a wrapper around ldap_search_s that retries depending on the error code this is supposed to catch dropped connections and auto-reconnect */ ADS_STATUS ads_do_search_retry(ADS_STRUCT *ads, const char *bind_path, int scope, const char *expr, const char **attrs, void **res) { ADS_STATUS status; int count = 3; char *bp; *res = NULL; if (!ads->ld && time(NULL) - ads->last_attempt < ADS_RECONNECT_TIME) { return ADS_ERROR(LDAP_SERVER_DOWN); } bp = strdup(bind_path); if (!bp) { return ADS_ERROR_NT(NT_STATUS_NO_MEMORY); } while (count--) { *res = NULL; status = ads_do_search_all(ads, bp, scope, expr, attrs, res); if (ADS_ERR_OK(status)) { DEBUG(5,("Search for %s gave %d replies\n", expr, ads_count_replies(ads, *res))); SAFE_FREE(bp); return status; } if (*res) ads_msgfree(ads, *res); *res = NULL; DEBUG(3,("Reopening ads connection to realm '%s' after error %s\n", ads->config.realm, ads_errstr(status))); if (ads->ld) { ldap_unbind(ads->ld); } ads->ld = NULL; status = ads_connect(ads); if (!ADS_ERR_OK(status)) { DEBUG(1,("ads_search_retry: failed to reconnect (%s)\n", ads_errstr(status))); ads_destroy(&ads); SAFE_FREE(bp); return status; } } SAFE_FREE(bp); if (!ADS_ERR_OK(status)) DEBUG(1,("ads reopen failed after error %s\n", ads_errstr(status))); return status; }
static ADS_STATUS do_krb5_kpasswd_request(krb5_context context, const char *kdc_host, uint16 pversion, krb5_creds *credsp, const char *princ, const char *newpw) { krb5_auth_context auth_context = NULL; krb5_data ap_req, chpw_req, chpw_rep; int ret, sock; socklen_t addr_len; struct sockaddr_storage remote_addr, local_addr; struct sockaddr_storage addr; krb5_address local_kaddr, remote_kaddr; bool use_tcp = False; if (!interpret_string_addr(&addr, kdc_host, 0)) { } ret = krb5_mk_req_extended(context, &auth_context, AP_OPTS_USE_SUBKEY, NULL, credsp, &ap_req); if (ret) { DEBUG(1,("krb5_mk_req_extended failed (%s)\n", error_message(ret))); return ADS_ERROR_KRB5(ret); } do { if (!use_tcp) { sock = open_udp_socket(kdc_host, DEFAULT_KPASSWD_PORT); if (sock == -1) { int rc = errno; SAFE_FREE(ap_req.data); krb5_auth_con_free(context, auth_context); DEBUG(1,("failed to open kpasswd socket to %s " "(%s)\n", kdc_host, strerror(errno))); return ADS_ERROR_SYSTEM(rc); } } else { NTSTATUS status; status = open_socket_out(&addr, DEFAULT_KPASSWD_PORT, LONG_CONNECT_TIMEOUT, &sock); if (!NT_STATUS_IS_OK(status)) { SAFE_FREE(ap_req.data); krb5_auth_con_free(context, auth_context); DEBUG(1,("failed to open kpasswd socket to %s " "(%s)\n", kdc_host, nt_errstr(status))); return ADS_ERROR_NT(status); } } addr_len = sizeof(remote_addr); if (getpeername(sock, (struct sockaddr *)&remote_addr, &addr_len) != 0) { close(sock); SAFE_FREE(ap_req.data); krb5_auth_con_free(context, auth_context); DEBUG(1,("getpeername() failed (%s)\n", error_message(errno))); return ADS_ERROR_SYSTEM(errno); } addr_len = sizeof(local_addr); if (getsockname(sock, (struct sockaddr *)&local_addr, &addr_len) != 0) { close(sock); SAFE_FREE(ap_req.data); krb5_auth_con_free(context, auth_context); DEBUG(1,("getsockname() failed (%s)\n", error_message(errno))); return ADS_ERROR_SYSTEM(errno); } if (!setup_kaddr(&remote_kaddr, &remote_addr) || !setup_kaddr(&local_kaddr, &local_addr)) { DEBUG(1,("do_krb5_kpasswd_request: " "Failed to setup addresses.\n")); close(sock); SAFE_FREE(ap_req.data); krb5_auth_con_free(context, auth_context); errno = EINVAL; return ADS_ERROR_SYSTEM(EINVAL); } ret = krb5_auth_con_setaddrs(context, auth_context, &local_kaddr, NULL); if (ret) { close(sock); SAFE_FREE(ap_req.data); krb5_auth_con_free(context, auth_context); DEBUG(1,("krb5_auth_con_setaddrs failed (%s)\n", error_message(ret))); return ADS_ERROR_KRB5(ret); } ret = build_kpasswd_request(pversion, context, auth_context, &ap_req, princ, newpw, use_tcp, &chpw_req); if (ret) { close(sock); SAFE_FREE(ap_req.data); krb5_auth_con_free(context, auth_context); DEBUG(1,("build_setpw_request failed (%s)\n", error_message(ret))); return ADS_ERROR_KRB5(ret); } ret = write(sock, chpw_req.data, chpw_req.length); if (ret != chpw_req.length) { close(sock); SAFE_FREE(chpw_req.data); SAFE_FREE(ap_req.data); krb5_auth_con_free(context, auth_context); DEBUG(1,("send of chpw failed (%s)\n", strerror(errno))); return ADS_ERROR_SYSTEM(errno); } SAFE_FREE(chpw_req.data); chpw_rep.length = 1500; chpw_rep.data = (char *) SMB_MALLOC(chpw_rep.length); if (!chpw_rep.data) { close(sock); SAFE_FREE(ap_req.data); krb5_auth_con_free(context, auth_context); DEBUG(1,("send of chpw failed (%s)\n", strerror(errno))); errno = ENOMEM; return ADS_ERROR_SYSTEM(errno); } ret = read(sock, chpw_rep.data, chpw_rep.length); if (ret < 0) { close(sock); SAFE_FREE(chpw_rep.data); SAFE_FREE(ap_req.data); krb5_auth_con_free(context, auth_context); DEBUG(1,("recv of chpw reply failed (%s)\n", strerror(errno))); return ADS_ERROR_SYSTEM(errno); } close(sock); chpw_rep.length = ret; ret = krb5_auth_con_setaddrs(context, auth_context, NULL,&remote_kaddr); if (ret) { SAFE_FREE(chpw_rep.data); SAFE_FREE(ap_req.data); krb5_auth_con_free(context, auth_context); DEBUG(1,("krb5_auth_con_setaddrs on reply failed (%s)\n", error_message(ret))); return ADS_ERROR_KRB5(ret); } ret = parse_setpw_reply(context, use_tcp, auth_context, &chpw_rep); SAFE_FREE(chpw_rep.data); if (ret) { if (ret == KRB5KRB_ERR_RESPONSE_TOO_BIG && !use_tcp) { DEBUG(5, ("Trying setpw with TCP!!!\n")); use_tcp = True; continue; } SAFE_FREE(ap_req.data); krb5_auth_con_free(context, auth_context); DEBUG(1,("parse_setpw_reply failed (%s)\n", error_message(ret))); return ADS_ERROR_KRB5(ret); } SAFE_FREE(ap_req.data); krb5_auth_con_free(context, auth_context); } while ( ret ); return ADS_SUCCESS; }
static ADS_STATUS ads_krb5_chg_password(const char *kdc_host, const char *principal, const char *oldpw, const char *newpw, int time_offset) { ADS_STATUS aret; krb5_error_code ret; krb5_context context = NULL; krb5_principal princ; krb5_get_init_creds_opt opts; krb5_creds creds; char *chpw_princ = NULL, *password; const char *realm = NULL; initialize_krb5_error_table(); ret = krb5_init_context(&context); if (ret) { DEBUG(1,("Failed to init krb5 context (%s)\n", error_message(ret))); return ADS_ERROR_KRB5(ret); } if ((ret = smb_krb5_parse_name(context, principal, &princ))) { krb5_free_context(context); DEBUG(1,("Failed to parse %s (%s)\n", principal, error_message(ret))); return ADS_ERROR_KRB5(ret); } krb5_get_init_creds_opt_init(&opts); krb5_get_init_creds_opt_set_tkt_life(&opts, 5*60); krb5_get_init_creds_opt_set_renew_life(&opts, 0); krb5_get_init_creds_opt_set_forwardable(&opts, 0); krb5_get_init_creds_opt_set_proxiable(&opts, 0); realm = smb_krb5_principal_get_realm(context, princ); /* We have to obtain an INITIAL changepw ticket for changing password */ if (asprintf(&chpw_princ, "kadmin/changepw@%s", realm) == -1) { krb5_free_context(context); DEBUG(1,("ads_krb5_chg_password: asprintf fail\n")); return ADS_ERROR_NT(NT_STATUS_NO_MEMORY); } password = SMB_STRDUP(oldpw); ret = krb5_get_init_creds_password(context, &creds, princ, password, kerb_prompter, NULL, 0, chpw_princ, &opts); SAFE_FREE(chpw_princ); SAFE_FREE(password); if (ret) { if (ret == KRB5KRB_AP_ERR_BAD_INTEGRITY) DEBUG(1,("Password incorrect while getting initial ticket")); else DEBUG(1,("krb5_get_init_creds_password failed (%s)\n", error_message(ret))); krb5_free_principal(context, princ); krb5_free_context(context); return ADS_ERROR_KRB5(ret); } aret = do_krb5_kpasswd_request(context, kdc_host, KRB5_KPASSWD_VERS_CHANGEPW, &creds, principal, newpw); krb5_free_principal(context, princ); krb5_free_context(context); return aret; }
ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods) { return ADS_ERROR_NT(NT_STATUS_NOT_IMPLEMENTED); }
/* this performs a SASL/gssapi bind we avoid using cyrus-sasl to make Samba more robust. cyrus-sasl is very dependent on correctly configured DNS whereas this routine is much less fragile see RFC2078 and RFC2222 for details */ static ADS_STATUS ads_sasl_gssapi_do_bind(ADS_STRUCT *ads, const gss_name_t serv_name) { uint32_t minor_status; gss_cred_id_t gss_cred = GSS_C_NO_CREDENTIAL; gss_ctx_id_t context_handle = GSS_C_NO_CONTEXT; gss_OID mech_type = GSS_C_NULL_OID; gss_buffer_desc output_token, input_token; uint32_t req_flags, ret_flags; int conf_state; struct berval cred; struct berval *scred = NULL; int i=0; int gss_rc, rc; uint8_t *p; uint32_t max_msg_size = ADS_SASL_WRAPPING_OUT_MAX_WRAPPED; uint8_t wrap_type = ADS_SASLWRAP_TYPE_PLAIN; ADS_STATUS status; struct ads_saslwrap *wrap = &ads->ldap_wrap_data; input_token.value = NULL; input_token.length = 0; status = ads_init_gssapi_cred(ads, &gss_cred); if (!ADS_ERR_OK(status)) { goto failed; } /* * Note: here we always ask the gssapi for sign and seal * as this is negotiated later after the mutal * authentication */ req_flags = GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG; for (i=0; i < MAX_GSS_PASSES; i++) { gss_rc = gss_init_sec_context(&minor_status, gss_cred, &context_handle, serv_name, mech_type, req_flags, 0, NULL, &input_token, NULL, &output_token, &ret_flags, NULL); if (scred) { ber_bvfree(scred); scred = NULL; } if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) { status = ADS_ERROR_GSS(gss_rc, minor_status); goto failed; } cred.bv_val = (char *)output_token.value; cred.bv_len = output_token.length; rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSSAPI", &cred, NULL, NULL, &scred); if (rc != LDAP_SASL_BIND_IN_PROGRESS) { status = ADS_ERROR(rc); goto failed; } if (output_token.value) { gss_release_buffer(&minor_status, &output_token); } if (scred) { input_token.value = scred->bv_val; input_token.length = scred->bv_len; } else { input_token.value = NULL; input_token.length = 0; } if (gss_rc == 0) break; } gss_rc = gss_unwrap(&minor_status,context_handle,&input_token,&output_token, &conf_state,NULL); if (scred) { ber_bvfree(scred); scred = NULL; } if (gss_rc) { status = ADS_ERROR_GSS(gss_rc, minor_status); goto failed; } p = (uint8_t *)output_token.value; #if 0 file_save("sasl_gssapi.dat", output_token.value, output_token.length); #endif if (p) { wrap_type = CVAL(p,0); SCVAL(p,0,0); max_msg_size = RIVAL(p,0); } gss_release_buffer(&minor_status, &output_token); if (!(wrap_type & wrap->wrap_type)) { /* * the server doesn't supports the wrap * type we want :-( */ DEBUG(0,("The ldap sasl wrap type doesn't match wanted[%d] server[%d]\n", wrap->wrap_type, wrap_type)); DEBUGADD(0,("You may want to set the 'client ldap sasl wrapping' option\n")); status = ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED); goto failed; } /* 0x58 is the minimum windows accepts */ if (max_msg_size < 0x58) { max_msg_size = 0x58; } 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_t *)output_token.value; RSIVAL(p,0,max_msg_size); SCVAL(p,0,wrap->wrap_type); /* * we used to add sprintf("dn:%s", ads->config.bind_path) here. * but using ads->config.bind_path is the wrong! It should be * the DN of the user object! * * w2k3 gives an error when we send an incorrect DN, but sending nothing * is ok and matches the information flow used in GSS-SPNEGO. */ gss_rc = gss_wrap(&minor_status, context_handle,0,GSS_C_QOP_DEFAULT, &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; } /* 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; rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSSAPI", &cred, NULL, NULL, &scred); gss_release_buffer(&minor_status, &input_token); status = ADS_ERROR(rc); if (!ADS_ERR_OK(status)) { goto failed; } if (wrap->wrap_type > ADS_SASLWRAP_TYPE_PLAIN) { gss_rc = gss_wrap_size_limit(&minor_status, context_handle, (wrap->wrap_type == ADS_SASLWRAP_TYPE_SEAL), GSS_C_QOP_DEFAULT, max_msg_size, &wrap->out.max_unwrapped); if (gss_rc) { status = ADS_ERROR_GSS(gss_rc, minor_status); goto failed; } wrap->out.sig_size = max_msg_size - wrap->out.max_unwrapped; wrap->in.min_wrapped = 0x2C; /* taken from a capture with LDAP unbind */ wrap->in.max_wrapped = ADS_SASL_WRAPPING_IN_MAX_WRAPPED; status = ads_setup_sasl_wrapping(wrap->wrap_private_data, ads->ldap.ld, &ads_sasl_gssapi_ops, context_handle); if (!ADS_ERR_OK(status)) { DEBUG(0, ("ads_setup_sasl_wrapping() failed: %s\n", ads_errstr(status))); goto failed; } /* make sure we don't free context_handle */ context_handle = GSS_C_NO_CONTEXT; } failed: if (gss_cred != GSS_C_NO_CREDENTIAL) gss_release_cred(&minor_status, &gss_cred); if (context_handle != GSS_C_NO_CONTEXT) gss_delete_sec_context(&minor_status, &context_handle, GSS_C_NO_BUFFER); if(scred) ber_bvfree(scred); return status; }
static ADS_STATUS ads_ranged_search_internal(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, int scope, const char *base, const char *filter, const char **attrs, void *args, const char *range_attr, char ***strings, size_t *num_strings, uint32_t *first_usn, int *num_retries, bool *more_values) { LDAPMessage *res = NULL; ADS_STATUS status; int count; uint32_t current_usn; DEBUG(10, ("Searching for attrs[0] = %s, attrs[1] = %s\n", attrs[0], attrs[1])); *more_values = False; status = ads_do_search_retry_internal(ads, base, scope, filter, attrs, args, &res); if (!ADS_ERR_OK(status)) { DEBUG(1,("ads_search: %s\n", ads_errstr(status))); return status; } if (!res) { return ADS_ERROR(LDAP_NO_MEMORY); } count = ads_count_replies(ads, res); if (count == 0) { ads_msgfree(ads, res); return ADS_ERROR(LDAP_SUCCESS); } if (*num_strings == 0) { if (!ads_pull_uint32(ads, res, "usnChanged", first_usn)) { DEBUG(1, ("could not pull first usnChanged!\n")); ads_msgfree(ads, res); return ADS_ERROR(LDAP_NO_MEMORY); } } if (!ads_pull_uint32(ads, res, "usnChanged", ¤t_usn)) { DEBUG(1, ("could not pull current usnChanged!\n")); ads_msgfree(ads, res); return ADS_ERROR(LDAP_NO_MEMORY); } if (*first_usn != current_usn) { DEBUG(5, ("USN on this record changed" " - restarting search\n")); if (*num_retries < 5) { (*num_retries)++; *num_strings = 0; ads_msgfree(ads, res); return ADS_ERROR_NT(STATUS_MORE_ENTRIES); } else { DEBUG(5, ("USN on this record changed" " - restarted search too many times, aborting!\n")); ads_msgfree(ads, res); return ADS_ERROR(LDAP_NO_MEMORY); } } *strings = ads_pull_strings_range(ads, mem_ctx, res, range_attr, *strings, &attrs[0], num_strings, more_values); ads_msgfree(ads, res); /* paranoia checks */ if (*strings == NULL && *more_values) { DEBUG(0,("no strings found but more values???\n")); return ADS_ERROR(LDAP_NO_MEMORY); } if (*num_strings == 0 && *more_values) { DEBUG(0,("no strings found but more values???\n")); return ADS_ERROR(LDAP_NO_MEMORY); } return (*more_values) ? ADS_ERROR_NT(STATUS_MORE_ENTRIES) : ADS_ERROR(LDAP_SUCCESS); }