*/
#include "includes.h"
+#include "ntdomain.h"
#include "winbindd.h"
#include "libsmb/namequery.h"
#include "../libcli/auth/libcli_auth.h"
+#include "libcli/auth/pam_errors.h"
#include "../librpc/gen_ndr/ndr_samr_c.h"
+#include "librpc/rpc/dcesrv_core.h"
#include "librpc/gen_ndr/ndr_winbind.h"
#include "rpc_client/cli_pipe.h"
#include "rpc_client/cli_samr.h"
return find_our_domain();
}
-static NTSTATUS fake_password_policy(struct winbindd_response *r,
- uint16_t validation_level,
- union netr_Validation *validation)
-{
- const struct netr_SamBaseInfo *bi = NULL;
- NTTIME min_password_age;
- NTTIME max_password_age;
-
- switch (validation_level) {
- case 3:
- bi = &validation->sam3->base;
- break;
- case 6:
- bi = &validation->sam6->base;
- break;
- default:
- return NT_STATUS_INTERNAL_ERROR;
- }
-
- if (bi->allow_password_change > bi->last_password_change) {
- min_password_age = bi->allow_password_change -
- bi->last_password_change;
- } else {
- min_password_age = 0;
- }
-
- if (bi->force_password_change > bi->last_password_change) {
- max_password_age = bi->force_password_change -
- bi->last_password_change;
- } else {
- max_password_age = 0;
- }
-
- r->data.auth.policy.min_length_password = 0;
- r->data.auth.policy.password_history = 0;
- r->data.auth.policy.password_properties = 0;
- r->data.auth.policy.expire =
- nt_time_to_unix_abs(&max_password_age);
- r->data.auth.policy.min_passwordage =
- nt_time_to_unix_abs(&min_password_age);
-
- return NT_STATUS_OK;
-}
-
static void fill_in_password_policy(struct winbindd_response *r,
const struct samr_DomInfo1 *p)
{
TALLOC_FREE(ui);
}
-enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
- struct winbindd_cli_state *state)
+NTSTATUS _wbint_PamAuth(struct pipes_struct *p,
+ struct wbint_PamAuth *r)
{
+ struct winbindd_domain *domain = wb_child_domain();
NTSTATUS result = NT_STATUS_LOGON_FAILURE;
NTSTATUS krb5_result = NT_STATUS_OK;
fstring name_namespace, name_domain, name_user;
- char *mapped_user;
- fstring domain_user;
+ char *mapped_user = NULL;
+ const char *domain_user = NULL;
uint16_t validation_level = UINT16_MAX;
union netr_Validation *validation = NULL;
+ struct netr_SamBaseInfo *base_info = NULL;
NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
bool ok;
uint64_t logon_id = 0;
const struct tsocket_address *remote = NULL;
const struct tsocket_address *local = NULL;
const char *krb5ccname = NULL;
+ uid_t uid;
+ pid_t client_pid;
- /* Ensure null termination */
- state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
+ if (domain == NULL) {
+ return NT_STATUS_REQUEST_NOT_ACCEPTED;
+ }
- /* Ensure null termination */
- state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
+ /* Cut client_pid to 32bit */
+ client_pid = r->in.client_pid;
+ if ((uint64_t)client_pid != r->in.client_pid) {
+ DBG_DEBUG("pid out of range\n");
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* Cut uid to 32bit */
+ uid = r->in.info->uid;
+ if ((uint64_t)uid != r->in.info->uid) {
+ DBG_DEBUG("uid out of range\n");
+ return NT_STATUS_INVALID_PARAMETER;
+ }
/*
* Generate a logon_id for this session.
*/
logon_id = generate_random_u64();
- remote = get_remote_address(state->mem_ctx, state->sock);
- local = get_local_address(state->mem_ctx, state->sock);
- DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
- state->request->data.auth.user));
+ remote = dcesrv_connection_get_remote_address(p->dce_call->conn);
+ local = dcesrv_connection_get_local_address(p->dce_call->conn);
+ DEBUG(3, ("[%"PRIu32"]: dual pam auth %s\n", client_pid,
+ r->in.info->username));
/* Parse domain and username */
- name_map_status = normalize_name_unmap(state->mem_ctx,
- state->request->data.auth.user,
+ name_map_status = normalize_name_unmap(p->mem_ctx,
+ r->in.info->username,
&mapped_user);
/* If the name normalization didn't actually do anything,
if (!NT_STATUS_IS_OK(name_map_status) &&
!NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
{
- mapped_user = state->request->data.auth.user;
+ mapped_user = discard_const(r->in.info->username);
}
ok = parse_domain_user(mapped_user,
name_user);
if (!ok) {
result = NT_STATUS_INVALID_PARAMETER;
- goto process_result;
+ goto done;
}
- if ( mapped_user != state->request->data.auth.user ) {
- fstr_sprintf( domain_user, "%s%c%s", name_domain,
- *lp_winbind_separator(),
- name_user );
- strlcpy( state->request->data.auth.user, domain_user,
- sizeof(state->request->data.auth.user));
+ if (mapped_user != r->in.info->username) {
+ domain_user = talloc_asprintf("%s%c%s",
+ name_domain,
+ *lp_winbind_separator(),
+ name_user);
+ if (domain_user == NULL) {
+ result = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ r->in.info->username = domain_user;
}
if (!domain->online) {
DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
/* Check for Kerberos authentication */
- if (domain->online && (state->request->flags & WBFLAG_PAM_KRB5)) {
+ if (domain->online && (r->in.flags & WBFLAG_PAM_KRB5)) {
result = winbindd_dual_pam_auth_kerberos(
domain,
- state->request->data.auth.user,
- state->request->data.auth.pass,
- state->request->data.auth.krb5_cc_type,
- get_uid_from_request(state->request),
- state->mem_ctx,
+ r->in.info->username,
+ r->in.info->password,
+ r->in.info->krb5_cc_type,
+ uid,
+ p->mem_ctx,
&validation_level,
&validation,
&krb5ccname);
if (NT_STATUS_IS_OK(result)) {
DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
-
- fstrcpy(state->response->data.auth.krb5ccname,
- krb5ccname);
-
goto process_result;
}
goto done;
}
- if (state->request->flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
+ if (r->in.flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
DEBUG(3,("falling back to samlogon\n"));
goto sam_logon;
} else {
sam_logon:
/* Check for Samlogon authentication */
if (domain->online) {
- struct netr_SamBaseInfo *base_info = NULL;
-
result = winbindd_dual_pam_auth_samlogon(
- state->mem_ctx, domain,
- state->request->data.auth.user,
- state->request->data.auth.pass,
+ p->mem_ctx,
+ domain,
+ r->in.info->username,
+ r->in.info->password,
logon_id,
- state->request->client_name,
- state->pid,
- state->request->flags,
+ r->in.client_name,
+ client_pid,
+ r->in.flags,
remote,
local,
&validation_level,
cached_logon:
/* Check for Cached logons */
- if (!domain->online && (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) &&
+ if (!domain->online && (r->in.flags & WBFLAG_PAM_CACHED_LOGIN) &&
lp_winbind_offline_logon()) {
result = winbindd_dual_pam_auth_cached(domain,
- (state->request->flags & WBFLAG_PAM_KRB5),
- state->request->data.auth.user,
- state->request->data.auth.pass,
- state->request->data.auth.krb5_cc_type,
- get_uid_from_request(state->request),
- state->mem_ctx,
+ (r->in.flags & WBFLAG_PAM_KRB5),
+ r->in.info->username,
+ r->in.info->password,
+ r->in.info->krb5_cc_type,
+ uid,
+ p->mem_ctx,
&validation_level,
&validation,
&krb5ccname);
goto done;
}
DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
-
- fstrcpy(state->response->data.auth.krb5ccname,
- krb5ccname);
}
process_result:
if (NT_STATUS_IS_OK(result)) {
struct dom_sid user_sid;
TALLOC_CTX *base_ctx = NULL;
- struct netr_SamBaseInfo *base_info = NULL;
struct netr_SamInfo3 *info3 = NULL;
- struct wbint_SidArray *sid_array = NULL;
switch (validation_level) {
case 3:
base_info = &validation->sam6->base;
break;
default:
+ DBG_ERR("Bad validation level %d\n", validation_level);
result = NT_STATUS_INTERNAL_ERROR;
goto done;
}
if (base_info->full_name.string == NULL) {
struct netr_SamInfo3 *cached_info3;
- cached_info3 = netsamlogon_cache_get(state->mem_ctx,
+ cached_info3 = netsamlogon_cache_get(p->mem_ctx,
&user_sid);
if (cached_info3 != NULL &&
cached_info3->base.full_name.string != NULL) {
/* Check if the user is in the right group */
- result = extra_data_to_sid_array(
- state->request->data.auth.require_membership_of_sid,
- state->mem_ctx,
- &sid_array);
+ result = check_info3_in_group(info3,
+ r->in.require_membership_of_sid);
if (!NT_STATUS_IS_OK(result)) {
- DBG_ERR("Failed to parse '%s' into a sid array: %s\n",
- state->request->data.auth.require_membership_of_sid,
- nt_errstr(result));
- goto done;
- }
-
- result = check_info3_in_group(info3, sid_array);
- if (!NT_STATUS_IS_OK(result)) {
- char *s = NDR_PRINT_STRUCT_STRING(state->mem_ctx,
- wbint_SidArray,
- sid_array);
+ char *s = NDR_PRINT_STRUCT_STRING(p->mem_ctx,
+ wbint_SidArray,
+ r->in.require_membership_of_sid);
DBG_NOTICE("User %s is not in the required groups:\n",
- state->request->data.auth.user);
+ r->in.info->username);
DEBUGADD(DBGLVL_NOTICE, ("%s", s));
DEBUGADD(DBGLVL_NOTICE,
("Plaintext authentication is rejected\n"));
- TALLOC_FREE(sid_array);
goto done;
}
- TALLOC_FREE(sid_array);
if (!is_allowed_domain(info3->base.logon_domain.string)) {
DBG_NOTICE("Authentication failed for user [%s] "
goto done;
}
- result = append_auth_data(state->mem_ctx, state->response,
- state->request->flags,
- validation_level,
- validation,
- name_domain, name_user);
- if (!NT_STATUS_IS_OK(result)) {
+ r->out.validation = talloc_zero(p->mem_ctx,
+ struct wbint_Validation);
+ if (r->out.validation == NULL) {
+ result = NT_STATUS_NO_MEMORY;
goto done;
}
- if ((state->request->flags & WBFLAG_PAM_CACHED_LOGIN)
+ r->out.validation->level = validation_level;
+ r->out.validation->validation = talloc_steal(r->out.validation,
+ validation);
+ r->out.validation->krb5ccname = talloc_steal(r->out.validation,
+ krb5ccname);
+ if ((r->in.flags & WBFLAG_PAM_CACHED_LOGIN)
&& lp_winbind_offline_logon()) {
result = winbindd_store_creds(domain,
- state->request->data.auth.user,
- state->request->data.auth.pass,
+ r->in.info->username,
+ r->in.info->password,
info3);
}
- if (state->request->flags & WBFLAG_PAM_GET_PWD_POLICY) {
- /*
- * WBFLAG_PAM_GET_PWD_POLICY is not used within
- * any Samba caller anymore.
- *
- * We just fake this based on the effective values
- * for the user, for legacy callers.
- */
- fake_password_policy(state->response,
- validation_level,
- validation);
- }
-
result = NT_STATUS_OK;
}
result = NT_STATUS_NO_LOGON_SERVERS;
}
- /*
- * Here we don't alter
- * state->response->data.auth.authoritative based
- * on the servers response
- * as we don't want a fallback to the local sam
- * for interactive PAM logons
- */
- set_auth_errors(state->response, result);
-
- DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
- state->request->data.auth.user,
- state->response->data.auth.nt_status_string,
- state->response->data.auth.pam_error));
+ DBG_PREFIX(NT_STATUS_IS_OK(result) ? 5 : 2,
+ ("Plain-text authentication for user %s returned %s"
+ " (PAM: %d)\n",
+ r->in.info->username,
+ nt_errstr(result),
+ nt_status_to_pam(result)));
/*
* Log the winbind pam authentication, the logon_id will tie this to
* any of the logons invoked from this request.
*/
+
log_authentication(
- state->mem_ctx,
+ p->mem_ctx,
domain,
- state->request->client_name,
- state->pid,
+ r->in.client_name,
+ client_pid,
validation_level,
validation,
start_time,
local,
result);
- return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
+ return result;
}
NTSTATUS winbind_dual_SamLogon(struct winbindd_domain *domain,
#include "libcli/security/dom_sid.h"
#include "lib/util/string_wrappers.h"
#include "lib/global_contexts.h"
+#include "librpc/gen_ndr/ndr_winbind_c.h"
+
+static NTSTATUS fake_password_policy(struct winbindd_response *r,
+ uint16_t validation_level,
+ union netr_Validation *validation)
+{
+ const struct netr_SamBaseInfo *bi = NULL;
+ NTTIME min_password_age;
+ NTTIME max_password_age;
+
+ switch (validation_level) {
+ case 3:
+ bi = &validation->sam3->base;
+ break;
+ case 6:
+ bi = &validation->sam6->base;
+ break;
+ default:
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ if (bi->allow_password_change > bi->last_password_change) {
+ min_password_age = bi->allow_password_change -
+ bi->last_password_change;
+ } else {
+ min_password_age = 0;
+ }
+
+ if (bi->force_password_change > bi->last_password_change) {
+ max_password_age = bi->force_password_change -
+ bi->last_password_change;
+ } else {
+ max_password_age = 0;
+ }
+
+ r->data.auth.policy.min_length_password = 0;
+ r->data.auth.policy.password_history = 0;
+ r->data.auth.policy.password_properties = 0;
+ r->data.auth.policy.expire =
+ nt_time_to_unix_abs(&max_password_age);
+ r->data.auth.policy.min_passwordage =
+ nt_time_to_unix_abs(&min_password_age);
+
+ return NT_STATUS_OK;
+}
struct winbindd_pam_auth_state {
- struct winbindd_request *request;
- struct winbindd_response *response;
+ struct wbint_PamAuth *r;
+ fstring name_namespace;
+ fstring name_domain;
+ fstring name_user;
};
static void winbindd_pam_auth_done(struct tevent_req *subreq);
struct tevent_req *req, *subreq;
struct winbindd_pam_auth_state *state;
struct winbindd_domain *domain;
- fstring name_namespace, name_domain, name_user;
char *mapped = NULL;
NTSTATUS status;
bool ok;
if (req == NULL) {
return NULL;
}
- state->request = request;
-
- /* Ensure null termination */
- request->data.auth.user[sizeof(request->data.auth.user)-1] = '\0';
- request->data.auth.pass[sizeof(request->data.auth.pass)-1] = '\0';
DBG_NOTICE("[%s (%u)]: pam auth %s\n",
cli->client_name,
}
ok = canonicalize_username(request->data.auth.user,
- name_namespace,
- name_domain,
- name_user);
+ state->name_namespace,
+ state->name_domain,
+ state->name_user);
if (!ok) {
tevent_req_nterror(req, NT_STATUS_NO_SUCH_USER);
return tevent_req_post(req, ev);
}
- domain = find_auth_domain(request->flags, name_namespace);
+ domain = find_auth_domain(request->flags, state->name_namespace);
if (domain == NULL) {
tevent_req_nterror(req, NT_STATUS_NO_SUCH_USER);
return tevent_req_post(req, ev);
}
- subreq = wb_domain_request_send(state, global_event_context(), domain,
- request);
+ state->r = talloc_zero(state, struct wbint_PamAuth);
+ if (tevent_req_nomem(state->r, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ state->r->in.client_name = talloc_strdup(
+ state->r, request->client_name);
+ if (tevent_req_nomem(state->r, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ state->r->in.client_pid = request->pid;
+ state->r->in.flags = request->flags;
+
+ state->r->in.info = talloc_zero(state->r, struct wbint_AuthUserInfo);
+ if (tevent_req_nomem(state->r, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ state->r->in.info->krb5_cc_type = talloc_strdup(
+ state->r, request->data.auth.krb5_cc_type);
+ if (tevent_req_nomem(state->r, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ state->r->in.info->password = talloc_strdup(
+ state->r, request->data.auth.pass);
+ if (tevent_req_nomem(state->r, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ state->r->in.info->username = talloc_strdup(
+ state->r, request->data.auth.user);
+ if (tevent_req_nomem(state->r, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ state->r->in.info->uid = request->data.auth.uid;
+
+ status = extra_data_to_sid_array(
+ request->data.auth.require_membership_of_sid,
+ state->r,
+ &state->r->in.require_membership_of_sid);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = dcerpc_wbint_PamAuth_r_send(state,
+ global_event_context(),
+ dom_child_handle(domain),
+ state->r);
if (tevent_req_nomem(subreq, req)) {
return tevent_req_post(req, ev);
}
subreq, struct tevent_req);
struct winbindd_pam_auth_state *state = tevent_req_data(
req, struct winbindd_pam_auth_state);
- int res, err;
+ NTSTATUS status;
- res = wb_domain_request_recv(subreq, state, &state->response, &err);
+ status = dcerpc_wbint_PamAuth_r_recv(subreq, state);
TALLOC_FREE(subreq);
- if (res == -1) {
- tevent_req_nterror(req, map_nt_error_from_unix(err));
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ if (tevent_req_nterror(req, state->r->out.result)) {
return;
}
+
tevent_req_done(req);
}
set_auth_errors(response, status);
return status;
}
- *response = *state->response;
+
response->result = WINBINDD_PENDING;
- state->response = talloc_move(response, &state->response);
- status = NT_STATUS(response->data.auth.nt_status);
- if (!NT_STATUS_IS_OK(status)) {
- return status;
- }
+ status = append_auth_data(response,
+ response,
+ state->r->in.flags,
+ state->r->out.validation->level,
+ state->r->out.validation->validation,
+ state->name_domain,
+ state->name_user);
+ fstrcpy(response->data.auth.krb5ccname,
+ state->r->out.validation->krb5ccname);
- if (state->request->flags & WBFLAG_PAM_INFO3_TEXT) {
+ if (state->r->in.flags & WBFLAG_PAM_INFO3_TEXT) {
bool ok;
ok = add_trusted_domain_from_auth(
- state->response->data.auth.validation_level,
- &state->response->data.auth.info3,
- &state->response->data.auth.info6);
+ state->r->out.validation->level,
+ &response->data.auth.info3,
+ &response->data.auth.info6);
if (!ok) {
DBG_ERR("add_trusted_domain_from_auth failed\n");
set_auth_errors(response, NT_STATUS_LOGON_FAILURE);
}
}
- if (state->request->flags & WBFLAG_PAM_CACHED_LOGIN) {
+ if (state->r->in.flags & WBFLAG_PAM_CACHED_LOGIN) {
/* Store in-memory creds for single-signon using ntlm_auth. */
status = winbindd_add_memory_creds(
- state->request->data.auth.user,
- get_uid_from_request(state->request),
- state->request->data.auth.pass);
+ state->r->in.info->username,
+ state->r->in.info->uid,
+ state->r->in.info->password);
DEBUG(10, ("winbindd_add_memory_creds returned: %s\n",
nt_errstr(status)));
}
- return status;
+ if (state->r->in.flags & WBFLAG_PAM_GET_PWD_POLICY) {
+ /*
+ * WBFLAG_PAM_GET_PWD_POLICY is not used within
+ * any Samba caller anymore.
+ *
+ * We just fake this based on the effective values
+ * for the user, for legacy callers.
+ */
+ status = fake_password_policy(response,
+ state->r->out.validation->level,
+ state->r->out.validation->validation);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("Failed to fake password policy: %s\n",
+ nt_errstr(status));
+ set_auth_errors(response, status);
+ return status;
+ }
+ }
+
+ return NT_STATUS_OK;
}