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) { return ADS_ERROR(LDAP_NO_MEMORY); } strlower_m(server); strupper_m(server_realm); asprintf(&princ, "ldap/%s@%s", server, server_realm); 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) { return ADS_ERROR(LDAP_NO_MEMORY); } strlower_m(server); strupper_m(server_realm); asprintf(&princ, "ldap/%s@%s", server, server_realm); 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; }
/* 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); }
ADS_STATUS ads_sasl_bind(ADS_STRUCT *ads) { const char *attrs[] = {"supportedSASLMechanisms", NULL}; char **values; ADS_STATUS status; int i, j; LDAPMessage *res; /* get a list of supported SASL mechanisms */ status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res); if (!ADS_ERR_OK(status)) return status; values = ldap_get_values(ads->ld, res, "supportedSASLMechanisms"); /* try our supported mechanisms in order */ for (i=0;sasl_mechanisms[i].name;i++) { /* see if the server supports it */ 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])); status = sasl_mechanisms[i].fn(ads); ldap_value_free(values); ldap_msgfree(res); return status; } } } ldap_value_free(values); ldap_msgfree(res); return ADS_ERROR(LDAP_AUTH_METHOD_NOT_SUPPORTED); }
/* find a printer given the name and the hostname Note that results "res" may be allocated on return so that the results can be used. It should be freed using ads_msgfree. */ ADS_STATUS ads_find_printer_on_server(ADS_STRUCT *ads, LDAPMessage **res, const char *printer, const char *servername) { ADS_STATUS status; char *srv_dn, **srv_cn, *s = NULL; const char *attrs[] = {"*", "nTSecurityDescriptor", NULL}; status = ads_find_machine_acct(ads, res, servername); if (!ADS_ERR_OK(status)) { DEBUG(1, ("ads_find_printer_on_server: cannot find host %s in ads\n", servername)); return status; } if (ads_count_replies(ads, *res) != 1) { ads_msgfree(ads, *res); *res = NULL; return ADS_ERROR(LDAP_NO_SUCH_OBJECT); } srv_dn = ldap_get_dn(ads->ldap.ld, *res); if (srv_dn == NULL) { ads_msgfree(ads, *res); *res = NULL; return ADS_ERROR(LDAP_NO_MEMORY); } srv_cn = ldap_explode_dn(srv_dn, 1); if (srv_cn == NULL) { ldap_memfree(srv_dn); ads_msgfree(ads, *res); *res = NULL; return ADS_ERROR(LDAP_INVALID_DN_SYNTAX); } ads_msgfree(ads, *res); *res = NULL; if (asprintf(&s, "(cn=%s-%s)", srv_cn[0], printer) == -1) { ldap_memfree(srv_dn); return ADS_ERROR(LDAP_NO_MEMORY); } status = ads_search(ads, res, s, attrs); ldap_memfree(srv_dn); ldap_value_free(srv_cn); SAFE_FREE(s); return status; }
ADS_STATUS ads_add_user_acct(ADS_STRUCT *ads, const char *user, const char *container, const char *fullname) { TALLOC_CTX *ctx; ADS_MODLIST mods; ADS_STATUS status; const char *upn, *new_dn, *name, *controlstr; char *name_escaped = NULL; const char *objectClass[] = {"top", "person", "organizationalPerson", "user", NULL}; if (fullname && *fullname) name = fullname; else name = user; if (!(ctx = talloc_init("ads_add_user_acct"))) return ADS_ERROR(LDAP_NO_MEMORY); status = ADS_ERROR(LDAP_NO_MEMORY); if (!(upn = talloc_asprintf(ctx, "%s@%s", user, ads->config.realm))) goto done; if (!(name_escaped = escape_rdn_val_string_alloc(name))) goto done; if (!(new_dn = talloc_asprintf(ctx, "cn=%s,%s,%s", name_escaped, container, ads->config.bind_path))) goto done; if (!(controlstr = talloc_asprintf(ctx, "%u", (UF_NORMAL_ACCOUNT | UF_ACCOUNTDISABLE)))) goto done; if (!(mods = ads_init_mods(ctx))) goto done; ads_mod_str(ctx, &mods, "cn", name); ads_mod_strlist(ctx, &mods, "objectClass", objectClass); ads_mod_str(ctx, &mods, "userPrincipalName", upn); ads_mod_str(ctx, &mods, "name", name); ads_mod_str(ctx, &mods, "displayName", name); ads_mod_str(ctx, &mods, "sAMAccountName", user); ads_mod_str(ctx, &mods, "userAccountControl", controlstr); status = ads_gen_add(ads, new_dn, mods); done: SAFE_FREE(name_escaped); talloc_destroy(ctx); return status; }
/* find a user account */ ADS_STATUS ads_find_user_acct(ADS_STRUCT *ads, LDAPMessage **res, const char *user) { ADS_STATUS status; char *ldap_exp; const char *attrs[] = {"*", NULL}; char *escaped_user = escape_ldap_string_alloc(user); if (!escaped_user) { return ADS_ERROR(LDAP_NO_MEMORY); } if (asprintf(&ldap_exp, "(samAccountName=%s)", escaped_user) == -1) { SAFE_FREE(escaped_user); return ADS_ERROR(LDAP_NO_MEMORY); } status = ads_search(ads, res, ldap_exp, attrs); SAFE_FREE(ldap_exp); SAFE_FREE(escaped_user); return status; }
static ADS_STATUS ads_generate_service_principal(ADS_STRUCT *ads, const char *given_principal, struct ads_service_principal *p) { ADS_STATUS status; #ifdef HAVE_KRB5 gss_buffer_desc input_name; /* GSS_KRB5_NT_PRINCIPAL_NAME */ gss_OID_desc nt_principal = {10, discard_const_p(char, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x01")}; uint32 minor_status; int gss_rc; #endif ZERO_STRUCTP(p); /* I've seen a child Windows 2000 domain not send 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, or when we are configured to ignore it - gd, abartlet */ if (!lp_client_use_spnego_principal() || !given_principal || strequal(given_principal, ADS_IGNORE_PRINCIPAL)) { status = ads_guess_service_principal(ads, &p->string); if (!ADS_ERR_OK(status)) { return status; } } else { p->string = SMB_STRDUP(given_principal); if (!p->string) { return ADS_ERROR(LDAP_NO_MEMORY); } } #ifdef HAVE_KRB5 input_name.value = p->string; input_name.length = strlen(p->string); gss_rc = gss_import_name(&minor_status, &input_name, &nt_principal, &p->name); if (gss_rc) { ads_free_service_principal(p); return ADS_ERROR_GSS(gss_rc, minor_status); } #endif return ADS_SUCCESS; }
void cmis_ad_flash_init(void) #if 0 { uint8_t ret = 0; g_flash_mem_map = CMIS_MALLOC(CMIS_ADS_FLASH_LEN); if(g_flash_mem_map == NULL) { ADS_ERROR("flash malloc ERROR\n"); while(1); } memset(g_flash_mem_map,0,CMIS_ADS_FLASH_LEN*sizeof(uint8_t)); app_porting_ads_flash_init(CMIS_ADS_FLASH_LEN/*+0x1000*/); g_flash_dirty_flag = 0; ret = app_porting_ads_flash_read_data(0,g_flash_mem_map,CMIS_ADS_FLASH_LEN); if(ret != 1) { ADS_ERROR("app_porting_ads_flash_read_data failed\n"); return; } }
ADS_STATUS ads_sasl_bind(ADS_STRUCT *ads) { const char *attrs[] = {"supportedSASLMechanisms", NULL}; char **values; ADS_STATUS status; int i, j; LDAPMessage *res; struct ads_saslwrap *wrap = &ads->ldap_wrap_data; /* get a list of supported SASL mechanisms */ status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res); if (!ADS_ERR_OK(status)) return status; values = ldap_get_values(ads->ldap.ld, res, "supportedSASLMechanisms"); if (ads->auth.flags & ADS_AUTH_SASL_SEAL) { wrap->wrap_type = ADS_SASLWRAP_TYPE_SEAL; } else if (ads->auth.flags & ADS_AUTH_SASL_SIGN) { wrap->wrap_type = ADS_SASLWRAP_TYPE_SIGN; } else { wrap->wrap_type = ADS_SASLWRAP_TYPE_PLAIN; } /* try our supported mechanisms in order */ for (i=0;sasl_mechanisms[i].name;i++) { /* see if the server supports it */ 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 && wrap->wrap_type == ADS_SASLWRAP_TYPE_PLAIN) { DEBUG(3,("SASL bin got LDAP_STRONG_AUTH_REQUIRED " "retrying with signing enabled\n")); wrap->wrap_type = ADS_SASLWRAP_TYPE_SIGN; goto retry; } ldap_value_free(values); ldap_msgfree(res); return status; } } } ldap_value_free(values); ldap_msgfree(res); return ADS_ERROR(LDAP_AUTH_METHOD_NOT_SUPPORTED); }
ADS_STATUS ads_do_search_all_sd_flags (ADS_STRUCT *ads, const char *dn, int scope, const char *filter, const char **attrs, uint32_t sd_flags, LDAPMessage **res) { int rv; struct ldb_request *req; struct ldb_control **controls; struct ldb_parse_tree *tree; struct ldb_dn *ldb_dn; controls = talloc_zero_array(ads, struct ldb_control *, 2); controls[0] = talloc(ads, struct ldb_control); controls[0]->oid = LDB_CONTROL_SD_FLAGS_OID; controls[0]->data = &sd_flags; controls[0]->critical = 1; tree = ldb_parse_tree(ads, filter); ldb_dn = ldb_dn_new(ads, ads->ldbctx, dn); rv = ldb_build_search_req_ex(&req, ads->ldbctx, (TALLOC_CTX *)res, ldb_dn, scope, tree, attrs, controls, res, ldb_search_default_callback, NULL); if (rv != LDB_SUCCESS) { talloc_free(*res); talloc_free(req); talloc_free(tree); return ADS_ERROR(rv); } rv = ldb_request(ads->ldbctx, req); if (rv == LDB_SUCCESS) { rv = ldb_wait(req->handle, LDB_WAIT_ALL); } talloc_free(req); talloc_free(tree); return ADS_ERROR(rv); }
ADS_STATUS ads_search_retry_sid(ADS_STRUCT *ads, LDAPMessage **res, const struct dom_sid *sid, const char **attrs) { char *dn, *sid_string; ADS_STATUS status; sid_string = sid_binstring_hex_talloc(talloc_tos(), sid); if (sid_string == NULL) { return ADS_ERROR(LDAP_NO_MEMORY); } if (!asprintf(&dn, "<SID=%s>", sid_string)) { TALLOC_FREE(sid_string); return ADS_ERROR(LDAP_NO_MEMORY); } status = ads_do_search_retry(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)", attrs, res); SAFE_FREE(dn); TALLOC_FREE(sid_string); return status; }
ADS_STATUS ads_add_group_acct(ADS_STRUCT *ads, const char *group, const char *container, const char *comment) { TALLOC_CTX *ctx; ADS_MODLIST mods; ADS_STATUS status; char *new_dn; char *name_escaped = NULL; const char *objectClass[] = {"top", "group", NULL}; if (!(ctx = talloc_init("ads_add_group_acct"))) return ADS_ERROR(LDAP_NO_MEMORY); status = ADS_ERROR(LDAP_NO_MEMORY); if (!(name_escaped = escape_rdn_val_string_alloc(group))) goto done; if (!(new_dn = talloc_asprintf(ctx, "cn=%s,%s,%s", name_escaped, container, ads->config.bind_path))) goto done; if (!(mods = ads_init_mods(ctx))) goto done; ads_mod_str(ctx, &mods, "cn", group); ads_mod_strlist(ctx, &mods, "objectClass",objectClass); ads_mod_str(ctx, &mods, "name", group); if (comment && *comment) ads_mod_str(ctx, &mods, "description", comment); ads_mod_str(ctx, &mods, "sAMAccountName", group); status = ads_gen_add(ads, new_dn, mods); done: SAFE_FREE(name_escaped); talloc_destroy(ctx); return status; }
static ADS_STATUS ads_guess_target(ADS_STRUCT *ads, char **service, char **hostname, char **principal) { ADS_STATUS status = ADS_ERROR(LDAP_NO_MEMORY); char *princ = NULL; TALLOC_CTX *frame; char *server = NULL; char *realm = NULL; int rc; frame = talloc_stackframe(); if (frame == NULL) { return ADS_ERROR(LDAP_NO_MEMORY); } if (ads->server.realm && ads->server.ldap_server) { server = strlower_talloc(frame, ads->server.ldap_server); if (server == NULL) { goto out; } realm = strupper_talloc(frame, ads->server.realm); if (realm == NULL) { goto out; } /* * If we got a name which is bigger than a NetBIOS name, * but isn't a FQDN, create one. */ if (strlen(server) > 15 && strstr(server, ".") == NULL) { char *dnsdomain; dnsdomain = strlower_talloc(frame, ads->server.realm); if (dnsdomain == NULL) { goto out; } server = talloc_asprintf(frame, "%s.%s", server, dnsdomain); if (server == NULL) { goto out; } } } else if (ads->config.realm && ads->config.ldap_server_name) { server = strlower_talloc(frame, ads->config.ldap_server_name); if (server == NULL) { goto out; } realm = strupper_talloc(frame, ads->config.realm); if (realm == NULL) { goto out; } /* * If we got a name which is bigger than a NetBIOS name, * but isn't a FQDN, create one. */ if (strlen(server) > 15 && strstr(server, ".") == NULL) { char *dnsdomain; dnsdomain = strlower_talloc(frame, ads->server.realm); if (dnsdomain == NULL) { goto out; } server = talloc_asprintf(frame, "%s.%s", server, dnsdomain); if (server == NULL) { goto out; } } } if (server == NULL || realm == NULL) { goto out; } *service = SMB_STRDUP("ldap"); if (*service == NULL) { status = ADS_ERROR(LDAP_PARAM_ERROR); goto out; } *hostname = SMB_STRDUP(server); if (*hostname == NULL) { SAFE_FREE(*service); status = ADS_ERROR(LDAP_PARAM_ERROR); goto out; } rc = asprintf(&princ, "ldap/%s@%s", server, realm); if (rc == -1 || princ == NULL) { SAFE_FREE(*service); SAFE_FREE(*hostname); status = ADS_ERROR(LDAP_PARAM_ERROR); goto out; } *principal = princ; status = ADS_SUCCESS; out: TALLOC_FREE(frame); return status; }
/* 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); }
/* 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; }
/* 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; struct auth_generic_state *auth_generic_state; 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); } 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 need to use seal here too */ 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; case ADS_SASLWRAP_TYPE_PLAIN: break; } 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 = 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 = spnego_gen_negTokenInit(talloc_tos(), OIDs_ntlm, &blob_out, NULL); } else { /* wrap it in SPNEGO */ msg1 = spnego_gen_auth(talloc_tos(), 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); } TALLOC_FREE(auth_generic_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 { TALLOC_FREE(auth_generic_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(talloc_tos(), blob, &blob_in, &tmp_blob)) { 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(talloc_tos(), blob, nt_status, OID_NTLMSSP, &blob_in)) { 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); } } data_blob_free(&blob); data_blob_free(&blob_out); turn++; } while (rc == LDAP_SASL_BIND_IN_PROGRESS && !NT_STATUS_IS_OK(nt_status)); if (ads->ldap.wrap_type > ADS_SASLWRAP_TYPE_PLAIN) { 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, 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); }
/* this performs a SASL/SPNEGO bind */ static ADS_STATUS ads_sasl_spnego_bind(ADS_STRUCT *ads) { struct berval *scred=NULL; int rc, i; ADS_STATUS status; DATA_BLOB blob; char *given_principal = NULL; char *OIDs[ASN1_MAX_OIDS]; #ifdef HAVE_KRB5 bool got_kerberos_mechanism = False; #endif rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSS-SPNEGO", NULL, NULL, NULL, &scred); if (rc != LDAP_SASL_BIND_IN_PROGRESS) { status = ADS_ERROR(rc); goto failed; } blob = data_blob(scred->bv_val, scred->bv_len); ber_bvfree(scred); #if 0 file_save("sasl_spnego.dat", blob.data, blob.length); #endif /* the server sent us the first part of the SPNEGO exchange in the negprot reply */ if (!spnego_parse_negTokenInit(blob, OIDs, &given_principal)) { data_blob_free(&blob); status = ADS_ERROR(LDAP_OPERATIONS_ERROR); goto failed; } data_blob_free(&blob); /* make sure the server understands kerberos */ for (i=0;OIDs[i];i++) { DEBUG(3,("ads_sasl_spnego_bind: got OID=%s\n", OIDs[i])); #ifdef HAVE_KRB5 if (strcmp(OIDs[i], OID_KERBEROS5_OLD) == 0 || strcmp(OIDs[i], OID_KERBEROS5) == 0) { got_kerberos_mechanism = True; } #endif talloc_free(OIDs[i]); } DEBUG(3,("ads_sasl_spnego_bind: got server principal name = %s\n", given_principal)); #ifdef HAVE_KRB5 if (!(ads->auth.flags & ADS_AUTH_DISABLE_KERBEROS) && got_kerberos_mechanism) { struct ads_service_principal p; status = ads_generate_service_principal(ads, given_principal, &p); TALLOC_FREE(given_principal); if (!ADS_ERR_OK(status)) { return status; } status = ads_sasl_spnego_krb5_bind(ads, &p); if (ADS_ERR_OK(status)) { ads_free_service_principal(&p); return status; } DEBUG(10,("ads_sasl_spnego_krb5_bind failed with: %s, " "calling kinit\n", ads_errstr(status))); status = ADS_ERROR_KRB5(ads_kinit_password(ads)); if (ADS_ERR_OK(status)) { status = ads_sasl_spnego_krb5_bind(ads, &p); if (!ADS_ERR_OK(status)) { DEBUG(0,("kinit succeeded but " "ads_sasl_spnego_krb5_bind failed: %s\n", ads_errstr(status))); } } ads_free_service_principal(&p); /* only fallback to NTLMSSP if allowed */ if (ADS_ERR_OK(status) || !(ads->auth.flags & ADS_AUTH_ALLOW_NTLMSSP)) { return status; } } else #endif { TALLOC_FREE(given_principal); } /* lets do NTLMSSP ... this has the big advantage that we don't need to sync clocks, and we don't rely on special versions of the krb5 library for HMAC_MD4 encryption */ return ads_sasl_spnego_ntlmssp_bind(ads); failed: return status; }
/* 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 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); }
/* a wrapper around ldap_search_s that retries depending on the error code this is supposed to catch dropped connections and auto-reconnect */ static ADS_STATUS ads_do_search_retry_internal(ADS_STRUCT *ads, const char *bind_path, int scope, const char *expr, const char **attrs, void *args, LDAPMessage **res) { ADS_STATUS status = ADS_SUCCESS; int count = 3; char *bp; *res = NULL; if (!ads->ldap.ld && time_mono(NULL) - ads->ldap.last_attempt < ADS_RECONNECT_TIME) { return ADS_ERROR(LDAP_SERVER_DOWN); } bp = SMB_STRDUP(bind_path); if (!bp) { return ADS_ERROR(LDAP_NO_MEMORY); } *res = NULL; /* when binding anonymously, we cannot use the paged search LDAP * control - Guenther */ if (ads->auth.flags & ADS_AUTH_ANON_BIND) { status = ads_do_search(ads, bp, scope, expr, attrs, res); } else { status = ads_do_search_all_args(ads, bp, scope, expr, attrs, args, res); } if (ADS_ERR_OK(status)) { DEBUG(5,("Search for %s in <%s> gave %d replies\n", expr, bp, ads_count_replies(ads, *res))); SAFE_FREE(bp); return status; } while (--count) { if (NT_STATUS_EQUAL(ads_ntstatus(status), NT_STATUS_IO_TIMEOUT) && ads->config.ldap_page_size >= 250) { int new_page_size = (ads->config.ldap_page_size / 2); DEBUG(1, ("Reducing LDAP page size from %d to %d due to IO_TIMEOUT\n", ads->config.ldap_page_size, new_page_size)); ads->config.ldap_page_size = new_page_size; } 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))); ads_disconnect(ads); 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; } *res = NULL; /* when binding anonymously, we cannot use the paged search LDAP * control - Guenther */ if (ads->auth.flags & ADS_AUTH_ANON_BIND) { status = ads_do_search(ads, bp, scope, expr, attrs, res); } else { status = ads_do_search_all_args(ads, bp, scope, expr, attrs, args, res); } if (ADS_ERR_OK(status)) { DEBUG(5,("Search for filter: %s, base: %s gave %d replies\n", expr, bp, ads_count_replies(ads, *res))); 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; }
/* 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); }
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); }
/** * @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; }
static void app_desai_ad_filter_notify(handle_t Filter, const uint8_t* Section, size_t Size) { uint16_t filterID = 0; uint16_t i = 0; int16_t pid = 0x1fff; uint16_t section_length = 0; bool bFlag = FALSE; uint8_t byReqID = 0; uint8_t* data = (uint8_t*)Section; int len = Size; ca_filter_t* sfilter = NULL; ca_filter_t filter = {0}; uint32_t ecm_crc32 = 0; uint8_t filter_disable =1; int32_t ret; int j=0; if(!Filter) { return; } if(!Section) { return; } if(!Size) { return; } ret = GxDemux_FilterGetPID(Filter,&pid); CHECK_RET(DEMUX,ret); while(len > 0) { //printf("DSAD rcv data,pid=0x%x,\n",pid); if(app_porting_ca_section_filter_crc32_check(data)) { printf("DSAD rcv data,crc error,\n"); break; } bFlag = FALSE; section_length = ((data[1] & 0x0F) << 8) + data[2] + 3; if(section_length > len) { ADS_ERROR("section len is wrong!!%d>%d\n",section_length,len); return; } for (filterID = 0; filterID< MAX_FILTER_COUNT; filterID++) { sfilter = app_porting_ca_demux_get_by_filter_id(filterID,&filter); if(!filter.usedStatus) { continue; } if(pid != filter.pid) { continue; } if(!filter.channelhandle || !filter.handle) { continue; } if(Filter != filter.handle) { continue; } /*NIT表或相应FILTER数据*/ for (i = 0; i<filter.filterLen; i++) { if ((data[i]&filter.mask[i]) != (filter.match[i]&filter.mask[i])) { printf("data[%d]=0x%x cafilter[filterID].match[%d]=0x%x \n", i,data[i],i,filter.match[i]); return; } } //sfilter->nms = 0; byReqID = filter.byReqID; bFlag = TRUE; break; } if (FALSE == bFlag) { ADS_FAIL(" FALSE == bFlag\n"); return; } if((filter.byReqID != DSAD_DELECMD_DATA) && (filter.byReqID != DSAD_CONFIG_DATA)) { //printf("DSAD rcv data,pid=0x%x,section_length=%d,reqId=%d\n",pid,section_length,filter.byReqID); } //printf("DSAD rcv data,pid=0x%x,section_length=%d,reqId=%d\n",pid,section_length,filter.byReqID); // GxDemux_FilterDisable(Filter); if(gpfDataNotifyFunction[filter.byReqID]) { //if((sfilter->byReqID == DSAD_DELECMD_DATA) ||(sfilter->byReqID == DSAD_CONFIG_DATA)) //ADS_Dbg("%s>>[filter->byReqID]= %d\n",__FUNCTION__,sfilter->byReqID); if(3 == sfilter->byReqID ) { ecm_crc32 = (data[section_length-4] <<24) | (data[section_length-3]<<16)| (data[section_length-2]<<8)| (data[section_length-1]); if(1)//(ecm_crc32 != DSAD_CONFIG_CRC) { DSAD_CONFIG_CRC = ecm_crc32; //ADS_Dbg("\n new config3,ecm_crc32bak =%x pid=0x%x,section_length=0x%x\n",data[0],pid,section_length); gpfDataNotifyFunction[sfilter->byReqID](pid,data,section_length); } else { if(filter_disable) { filter_disable =0; GxDemux_FilterDisable(Filter); } //ADS_Dbg("\n same config,ecm_crc32bak =%x pid=0x%x,section_length=0x%x\n",data[0],pid,section_length); //return ; } } else if(2 == sfilter->byReqID ) { ecm_crc32 = (data[section_length-4] <<24) | (data[section_length-3]<<16)| (data[section_length-2]<<8)| (data[section_length-1]); if(1)//(ecm_crc32 != DSAD_DELECMD_CRC) { DSAD_DELECMD_CRC = ecm_crc32; gpfDataNotifyFunction[sfilter->byReqID](pid,data,section_length); //ADS_Dbg("\n new config2,ecm_crc32bak =%x pid=0x%x,section_length=0x%x\n",data[0],pid,section_length); } else { if(filter_disable) { filter_disable =0; GxDemux_FilterDisable(Filter); } //ADS_Dbg("\n same DELECMD,ecm_crc32bak =%x pid=0x%x,section_length=0x%x\n",data[0],pid,section_length); //return ; } } else { gpfDataNotifyFunction[sfilter->byReqID](pid,data,section_length); } } j ++; data += section_length; len -= section_length; } return; }
/* this performs a SASL/SPNEGO bind */ static ADS_STATUS ads_sasl_spnego_bind(ADS_STRUCT *ads) { TALLOC_CTX *frame = talloc_stackframe(); struct ads_service_principal p = {0}; struct berval *scred=NULL; int rc, i; ADS_STATUS status; DATA_BLOB blob = data_blob_null; char *given_principal = NULL; char *OIDs[ASN1_MAX_OIDS]; #ifdef HAVE_KRB5 bool got_kerberos_mechanism = False; #endif const char *mech = NULL; rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSS-SPNEGO", NULL, NULL, NULL, &scred); if (rc != LDAP_SASL_BIND_IN_PROGRESS) { status = ADS_ERROR(rc); goto done; } blob = data_blob(scred->bv_val, scred->bv_len); ber_bvfree(scred); #if 0 file_save("sasl_spnego.dat", blob.data, blob.length); #endif /* the server sent us the first part of the SPNEGO exchange in the negprot reply */ if (!spnego_parse_negTokenInit(talloc_tos(), blob, OIDs, &given_principal, NULL) || OIDs[0] == NULL) { status = ADS_ERROR(LDAP_OPERATIONS_ERROR); goto done; } TALLOC_FREE(given_principal); /* make sure the server understands kerberos */ for (i=0;OIDs[i];i++) { DEBUG(3,("ads_sasl_spnego_bind: got OID=%s\n", OIDs[i])); #ifdef HAVE_KRB5 if (strcmp(OIDs[i], OID_KERBEROS5_OLD) == 0 || strcmp(OIDs[i], OID_KERBEROS5) == 0) { got_kerberos_mechanism = True; } #endif talloc_free(OIDs[i]); } status = ads_generate_service_principal(ads, &p); if (!ADS_ERR_OK(status)) { goto done; } #ifdef HAVE_KRB5 if (!(ads->auth.flags & ADS_AUTH_DISABLE_KERBEROS) && got_kerberos_mechanism) { mech = "KRB5"; if (ads->auth.password == NULL || ads->auth.password[0] == '\0') { status = ads_sasl_spnego_gensec_bind(ads, "GSS-SPNEGO", CRED_MUST_USE_KERBEROS, p.service, p.hostname, blob); if (ADS_ERR_OK(status)) { ads_free_service_principal(&p); goto done; } DEBUG(10,("ads_sasl_spnego_gensec_bind(KRB5) failed with: %s, " "calling kinit\n", ads_errstr(status))); } status = ADS_ERROR_KRB5(ads_kinit_password(ads)); if (ADS_ERR_OK(status)) { status = ads_sasl_spnego_gensec_bind(ads, "GSS-SPNEGO", CRED_MUST_USE_KERBEROS, p.service, p.hostname, blob); if (!ADS_ERR_OK(status)) { DEBUG(0,("kinit succeeded but " "ads_sasl_spnego_gensec_bind(KRB5) failed " "for %s/%s with user[%s] realm[%s]: %s\n", p.service, p.hostname, ads->auth.user_name, ads->auth.realm, ads_errstr(status))); } } /* only fallback to NTLMSSP if allowed */ if (ADS_ERR_OK(status) || !(ads->auth.flags & ADS_AUTH_ALLOW_NTLMSSP)) { goto done; } DEBUG(1,("ads_sasl_spnego_gensec_bind(KRB5) failed " "for %s/%s with user[%s] realm[%s]: %s, " "fallback to NTLMSSP\n", p.service, p.hostname, ads->auth.user_name, ads->auth.realm, ads_errstr(status))); } #endif /* lets do NTLMSSP ... this has the big advantage that we don't need to sync clocks, and we don't rely on special versions of the krb5 library for HMAC_MD4 encryption */ mech = "NTLMSSP"; status = ads_sasl_spnego_gensec_bind(ads, "GSS-SPNEGO", CRED_DONT_USE_KERBEROS, p.service, p.hostname, data_blob_null); done: if (!ADS_ERR_OK(status)) { DEBUG(1,("ads_sasl_spnego_gensec_bind(%s) failed " "for %s/%s with user[%s] realm=[%s]: %s\n", mech, p.service, p.hostname, ads->auth.user_name, ads->auth.realm, ads_errstr(status))); } ads_free_service_principal(&p); TALLOC_FREE(frame); if (blob.data != NULL) { data_blob_free(&blob); } return status; }
/* 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; }
/* 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; }
/* 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_bind(ADS_STRUCT *ads) { uint32 minor_status; gss_name_t serv_name; gss_buffer_desc input_name; 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 ret_flags, conf_state; struct berval cred; struct berval *scred = NULL; int i=0; int gss_rc, rc; uint8 *p; uint32 max_msg_size = 0; char *sname = NULL; ADS_STATUS status; krb5_principal principal = NULL; krb5_context ctx = NULL; krb5_enctype enc_types[] = { #ifdef ENCTYPE_ARCFOUR_HMAC ENCTYPE_ARCFOUR_HMAC, #endif ENCTYPE_DES_CBC_MD5, ENCTYPE_NULL}; gss_OID_desc nt_principal = {10, CONST_DISCARD(char *, "\052\206\110\206\367\022\001\002\002\002")}; /* we need to fetch a service ticket as the ldap user in the servers realm, regardless of our realm */ asprintf(&sname, "ldap/%s@%s", ads->config.ldap_server_name, ads->config.realm); initialize_krb5_error_table(); status = ADS_ERROR_KRB5(krb5_init_context(&ctx)); if (!ADS_ERR_OK(status)) { SAFE_FREE(sname); return status; } status = ADS_ERROR_KRB5(krb5_set_default_tgs_ktypes(ctx, enc_types)); if (!ADS_ERR_OK(status)) { SAFE_FREE(sname); krb5_free_context(ctx); return status; } status = ADS_ERROR_KRB5(smb_krb5_parse_name(ctx, sname, &principal)); if (!ADS_ERR_OK(status)) { SAFE_FREE(sname); krb5_free_context(ctx); return status; } input_name.value = &principal; input_name.length = sizeof(principal); gss_rc = gss_import_name(&minor_status, &input_name, &nt_principal, &serv_name); /* * The MIT libraries have a *HORRIBLE* bug - input_value.value needs * to point to the *address* of the krb5_principal, and the gss libraries * to a shallow copy of the krb5_principal pointer - so we need to keep * the krb5_principal around until we do the gss_release_name. MIT *SUCKS* ! * Just one more way in which MIT engineers screwed me over.... JRA. */ SAFE_FREE(sname); if (gss_rc) { krb5_free_principal(ctx, principal); krb5_free_context(ctx); return ADS_ERROR_GSS(gss_rc, minor_status); } input_token.value = NULL; input_token.length = 0; for (i=0; i < MAX_GSS_PASSES; i++) { gss_rc = gss_init_sec_context(&minor_status, GSS_C_NO_CREDENTIAL, &context_handle, serv_name, mech_type, GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG, 0, NULL, &input_token, NULL, &output_token, &ret_flags, NULL); if (input_token.value) { gss_release_buffer(&minor_status, &input_token); } 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->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, (int *)&conf_state,NULL); if (gss_rc) { status = ADS_ERROR_GSS(gss_rc, minor_status); goto failed; } gss_release_buffer(&minor_status, &input_token); p = (uint8 *)output_token.value; #if 0 file_save("sasl_gssapi.dat", output_token.value, output_token.length); #endif if (p) { max_msg_size = (p[1]<<16) | (p[2]<<8) | p[3]; } gss_release_buffer(&minor_status, &output_token); output_token.value = SMB_MALLOC(strlen(ads->config.bind_path) + 8); p = (uint8 *)output_token.value; *p++ = 1; /* no sign & seal selection */ /* choose the same size as the server gave us */ *p++ = max_msg_size>>16; *p++ = max_msg_size>>8; *p++ = max_msg_size; snprintf((char *)p, strlen(ads->config.bind_path)+4, "dn:%s", ads->config.bind_path); p += strlen((const char *)p); output_token.length = PTR_DIFF(p, output_token.value); gss_rc = gss_wrap(&minor_status, context_handle,0,GSS_C_QOP_DEFAULT, &output_token, (int *)&conf_state, &input_token); if (gss_rc) { status = ADS_ERROR_GSS(gss_rc, minor_status); goto failed; } free(output_token.value); cred.bv_val = (char *)input_token.value; cred.bv_len = input_token.length; rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL, &scred); status = ADS_ERROR(rc); gss_release_buffer(&minor_status, &input_token); failed: gss_release_name(&minor_status, &serv_name); if (context_handle != GSS_C_NO_CONTEXT) gss_delete_sec_context(&minor_status, &context_handle, GSS_C_NO_BUFFER); krb5_free_principal(ctx, principal); krb5_free_context(ctx); if(scred) ber_bvfree(scred); return status; }