summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Bartlett <abartlet@samba.org>2009-06-18 11:08:46 +1000
committerAndrew Bartlett <abartlet@samba.org>2009-06-18 13:49:30 +1000
commit19413c52495877d54c90c60229568d0077fda30b (patch)
treec148e96ba2ff28933f2d5f3714b8fc7e60957dec
parent2afc6df9b49a246129acdd7c8c24448c8cf3b6ef (diff)
downloadsamba-19413c52495877d54c90c60229568d0077fda30b.tar.gz
s4:kdc Allow a password change when the password is expired
This requires a rework on Heimdal's windc plugin layer, as we want full control over what tickets Heimdal will issue. (In particular, in case our requirements become more complex in future). The original problem was that Heimdal's check would permit the ticket, but Samba would then deny it, not knowing it was for kadmin/changepw Also (in hdb-samba4) be a bit more careful on what entries we will make the 'change_pw' service mark that this depends on. Andrew Bartlett
-rw-r--r--source4/auth/auth.h3
-rw-r--r--source4/auth/ntlm/auth_sam.c2
-rw-r--r--source4/auth/sam.c11
-rw-r--r--source4/heimdal/kdc/headers.h1
-rw-r--r--source4/heimdal/kdc/kdc_locl.h1
-rw-r--r--source4/heimdal/kdc/kerberos5.c22
-rw-r--r--source4/heimdal/kdc/krb5tgs.c8
-rw-r--r--source4/heimdal/kdc/windc.c22
-rw-r--r--source4/heimdal/kdc/windc_plugin.h8
-rw-r--r--source4/kdc/hdb-samba4.c13
-rw-r--r--source4/kdc/kdc.h2
-rw-r--r--source4/kdc/pac-glue.c75
12 files changed, 98 insertions, 70 deletions
diff --git a/source4/auth/auth.h b/source4/auth/auth.h
index f6d739325da..6bad0178629 100644
--- a/source4/auth/auth.h
+++ b/source4/auth/auth.h
@@ -232,7 +232,8 @@ NTSTATUS authsam_account_ok(TALLOC_CTX *mem_ctx,
struct ldb_message *msg,
const char *logon_workstation,
const char *name_for_logs,
- bool allow_domain_trust);
+ bool allow_domain_trust,
+ bool password_change);
struct auth_session_info *system_session(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx);
NTSTATUS authsam_make_server_info(TALLOC_CTX *mem_ctx, struct ldb_context *sam_ctx,
const char *netbios_name,
diff --git a/source4/auth/ntlm/auth_sam.c b/source4/auth/ntlm/auth_sam.c
index 0bb79e234ce..253ddf2286d 100644
--- a/source4/auth/ntlm/auth_sam.c
+++ b/source4/auth/ntlm/auth_sam.c
@@ -175,7 +175,7 @@ static NTSTATUS authsam_authenticate(struct auth_context *auth_context,
msg,
user_info->workstation_name,
user_info->mapped.account_name,
- false);
+ false, false);
return nt_status;
}
diff --git a/source4/auth/sam.c b/source4/auth/sam.c
index 68eaacf2550..acbd50c3fa0 100644
--- a/source4/auth/sam.c
+++ b/source4/auth/sam.c
@@ -147,7 +147,8 @@ _PUBLIC_ NTSTATUS authsam_account_ok(TALLOC_CTX *mem_ctx,
struct ldb_message *msg,
const char *logon_workstation,
const char *name_for_logs,
- bool allow_domain_trust)
+ bool allow_domain_trust,
+ bool password_change)
{
uint16_t acct_flags;
const char *workstation_list;
@@ -189,15 +190,15 @@ _PUBLIC_ NTSTATUS authsam_account_ok(TALLOC_CTX *mem_ctx,
return NT_STATUS_ACCOUNT_EXPIRED;
}
- /* check for immediate expiry "must change at next logon" */
- if (must_change_time == 0) {
+ /* check for immediate expiry "must change at next logon" (but not if this is a password change request) */
+ if ((must_change_time == 0) && !password_change) {
DEBUG(1,("sam_account_ok: Account for user '%s' password must change!.\n",
name_for_logs));
return NT_STATUS_PASSWORD_MUST_CHANGE;
}
- /* check for expired password */
- if (must_change_time < now) {
+ /* check for expired password (but not if this is a password change request) */
+ if ((must_change_time < now) && !password_change) {
DEBUG(1,("sam_account_ok: Account for user '%s' password expired!.\n",
name_for_logs));
DEBUG(1,("sam_account_ok: Password expired at '%s' unix time.\n",
diff --git a/source4/heimdal/kdc/headers.h b/source4/heimdal/kdc/headers.h
index 2240336e31f..b9a828852a6 100644
--- a/source4/heimdal/kdc/headers.h
+++ b/source4/heimdal/kdc/headers.h
@@ -104,6 +104,7 @@
#ifndef NO_NTLM
#include <heimntlm.h>
#endif
+#include <kdc.h>
#include <windc_plugin.h>
#undef ALLOC
diff --git a/source4/heimdal/kdc/kdc_locl.h b/source4/heimdal/kdc/kdc_locl.h
index 9b291ac8965..daf155839cd 100644
--- a/source4/heimdal/kdc/kdc_locl.h
+++ b/source4/heimdal/kdc/kdc_locl.h
@@ -39,7 +39,6 @@
#define __KDC_LOCL_H__
#include "headers.h"
-#include "kdc.h"
typedef struct pk_client_params pk_client_params;
struct DigestREQ;
diff --git a/source4/heimdal/kdc/kerberos5.c b/source4/heimdal/kdc/kerberos5.c
index 941a2e0572e..ac495b1ac7b 100644
--- a/source4/heimdal/kdc/kerberos5.c
+++ b/source4/heimdal/kdc/kerberos5.c
@@ -668,11 +668,11 @@ log_as_req(krb5_context context,
*/
krb5_error_code
-_kdc_check_flags(krb5_context context,
- krb5_kdc_configuration *config,
- hdb_entry_ex *client_ex, const char *client_name,
- hdb_entry_ex *server_ex, const char *server_name,
- krb5_boolean is_as_req)
+kdc_check_flags(krb5_context context,
+ krb5_kdc_configuration *config,
+ hdb_entry_ex *client_ex, const char *client_name,
+ hdb_entry_ex *server_ex, const char *server_name,
+ krb5_boolean is_as_req)
{
if(client_ex != NULL) {
hdb_entry *client = &client_ex->entry;
@@ -921,7 +921,6 @@ _kdc_as_rep(krb5_context context,
"AS-REQ malformed server name from %s", from);
goto out;
}
-
if(b->cname == NULL){
ret = KRB5KRB_ERR_GENERIC;
e_text = "No client in request";
@@ -1345,14 +1344,9 @@ _kdc_as_rep(krb5_context context,
* with in a preauth mech.
*/
- ret = _kdc_check_flags(context, config,
- client, client_name,
- server, server_name,
- TRUE);
- if(ret)
- goto out;
-
- ret = _kdc_windc_client_access(context, client, req, &e_data);
+ ret = _kdc_check_access(context, config, client, client_name,
+ server, server_name,
+ req, &e_data);
if(ret)
goto out;
diff --git a/source4/heimdal/kdc/krb5tgs.c b/source4/heimdal/kdc/krb5tgs.c
index 3abdb18ae48..59104da3d60 100644
--- a/source4/heimdal/kdc/krb5tgs.c
+++ b/source4/heimdal/kdc/krb5tgs.c
@@ -1860,10 +1860,10 @@ server_lookup:
* Check flags
*/
- ret = _kdc_check_flags(context, config,
- client, cpn,
- server, spn,
- FALSE);
+ ret = kdc_check_flags(context, config,
+ client, cpn,
+ server, spn,
+ FALSE);
if(ret)
goto out;
diff --git a/source4/heimdal/kdc/windc.c b/source4/heimdal/kdc/windc.c
index fe3cd997e76..9d7fa52ceab 100644
--- a/source4/heimdal/kdc/windc.c
+++ b/source4/heimdal/kdc/windc.c
@@ -99,12 +99,22 @@ _kdc_pac_verify(krb5_context context,
}
krb5_error_code
-_kdc_windc_client_access(krb5_context context,
- struct hdb_entry_ex *client,
- KDC_REQ *req,
- krb5_data *e_data)
+_kdc_check_access(krb5_context context,
+ krb5_kdc_configuration *config,
+ hdb_entry_ex *client_ex, const char *client_name,
+ hdb_entry_ex *server_ex, const char *server_name,
+ KDC_REQ *req,
+ krb5_data *e_data)
{
if (windcft == NULL)
- return 0;
- return (windcft->client_access)(windcctx, context, client, req, e_data);
+ return kdc_check_flags(context, config,
+ client_ex, client_name,
+ server_ex, server_name,
+ req->msg_type == krb_as_req);
+
+ return (windcft->client_access)(windcctx,
+ context, config,
+ client_ex, client_name,
+ server_ex, server_name,
+ req, e_data);
}
diff --git a/source4/heimdal/kdc/windc_plugin.h b/source4/heimdal/kdc/windc_plugin.h
index 34016694b25..c7efb7b8524 100644
--- a/source4/heimdal/kdc/windc_plugin.h
+++ b/source4/heimdal/kdc/windc_plugin.h
@@ -64,10 +64,14 @@ typedef krb5_error_code
typedef krb5_error_code
(*krb5plugin_windc_client_access)(
- void *, krb5_context, struct hdb_entry_ex *, KDC_REQ *, krb5_data *);
+ void *, krb5_context,
+ krb5_kdc_configuration *config,
+ hdb_entry_ex *, const char *,
+ hdb_entry_ex *, const char *,
+ KDC_REQ *, krb5_data *);
-#define KRB5_WINDC_PLUGING_MINOR 3
+#define KRB5_WINDC_PLUGING_MINOR 4
typedef struct krb5plugin_windc_ftable {
int minor_version;
diff --git a/source4/kdc/hdb-samba4.c b/source4/kdc/hdb-samba4.c
index c0fa5132d16..eda7867bb5e 100644
--- a/source4/kdc/hdb-samba4.c
+++ b/source4/kdc/hdb-samba4.c
@@ -627,7 +627,18 @@ static krb5_error_code LDB_message2entry(krb5_context context, HDB *db,
entry_ex->entry.flags.invalid = 0;
entry_ex->entry.flags.server = 1;
- entry_ex->entry.flags.change_pw = 1;
+
+ /* Don't mark all requests for the krbtgt/realm as
+ * 'change password', as otherwise we could get into
+ * trouble, and not enforce the password expirty.
+ * Instead, only do it when request is for the kpasswd service */
+ if (ent_type == HDB_SAMBA4_ENT_TYPE_SERVER
+ && principal->name.name_string.len == 2
+ && (strcmp(principal->name.name_string.val[0], "kadmin") == 0)
+ && (strcmp(principal->name.name_string.val[1], "changepw") == 0)
+ && lp_is_my_domain_or_realm(lp_ctx, principal->realm)) {
+ entry_ex->entry.flags.change_pw = 1;
+ }
entry_ex->entry.flags.client = 0;
entry_ex->entry.flags.forwardable = 1;
entry_ex->entry.flags.ok_as_delegate = 1;
diff --git a/source4/kdc/kdc.h b/source4/kdc/kdc.h
index a281e1d9c9e..d37a32ebbfc 100644
--- a/source4/kdc/kdc.h
+++ b/source4/kdc/kdc.h
@@ -22,8 +22,8 @@
#include "system/kerberos.h"
#include "auth/kerberos/kerberos.h"
-#include <kdc.h>
#include <hdb.h>
+#include <kdc.h>
#include <krb5/windc_plugin.h>
#include "kdc/pac_glue.h"
diff --git a/source4/kdc/pac-glue.c b/source4/kdc/pac-glue.c
index 411e752c04c..5bd4cb10c73 100644
--- a/source4/kdc/pac-glue.c
+++ b/source4/kdc/pac-glue.c
@@ -231,28 +231,29 @@ static void samba_kdc_build_edata_reply(TALLOC_CTX *tmp_ctx, krb5_data *e_data,
krb5_error_code samba_kdc_check_client_access(void *priv,
- krb5_context context, hdb_entry_ex *entry_ex,
+ krb5_context context,
+ krb5_kdc_configuration *config,
+ hdb_entry_ex *client_ex, const char *client_name,
+ hdb_entry_ex *server_ex, const char *server_name,
KDC_REQ *req,
krb5_data *e_data)
{
krb5_error_code ret;
NTSTATUS nt_status;
- TALLOC_CTX *tmp_ctx = talloc_new(entry_ex->ctx);
- struct hdb_ldb_private *p = talloc_get_type(entry_ex->ctx, struct hdb_ldb_private);
- char *name, *workstation = NULL;
+ TALLOC_CTX *tmp_ctx;
+ struct hdb_ldb_private *p;
+ char *workstation = NULL;
HostAddresses *addresses = req->req_body.addresses;
int i;
+ bool password_change;
+
+ tmp_ctx = talloc_new(client_ex->ctx);
+ p = talloc_get_type(client_ex->ctx, struct hdb_ldb_private);
if (!tmp_ctx) {
return ENOMEM;
}
- ret = krb5_unparse_name(context, entry_ex->entry.principal, &name);
- if (ret != 0) {
- talloc_free(tmp_ctx);
- return ret;
- }
-
if (addresses) {
for (i=0; i < addresses->len; i++) {
if (addresses->val->addr_type == KRB5_ADDRESS_NETBIOS) {
@@ -272,6 +273,8 @@ krb5_error_code samba_kdc_check_client_access(void *priv,
}
}
+ password_change = (server_ex && server_ex->entry.flags.change_pw);
+
/* we allow all kinds of trusts here */
nt_status = authsam_account_ok(tmp_ctx,
p->samdb,
@@ -279,30 +282,34 @@ krb5_error_code samba_kdc_check_client_access(void *priv,
p->realm_dn,
p->msg,
workstation,
- name, true);
- free(name);
-
- if (NT_STATUS_IS_OK(nt_status))
- return 0;
-
- if (NT_STATUS_EQUAL(nt_status, NT_STATUS_PASSWORD_MUST_CHANGE))
- ret = KRB5KDC_ERR_KEY_EXPIRED;
- else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_PASSWORD_EXPIRED))
- ret = KRB5KDC_ERR_KEY_EXPIRED;
- else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCOUNT_EXPIRED))
- ret = KRB5KDC_ERR_CLIENT_REVOKED;
- else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCOUNT_DISABLED))
- ret = KRB5KDC_ERR_CLIENT_REVOKED;
- else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_INVALID_LOGON_HOURS))
- ret = KRB5KDC_ERR_CLIENT_REVOKED;
- else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCOUNT_LOCKED_OUT))
- ret = KRB5KDC_ERR_CLIENT_REVOKED;
- else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_INVALID_WORKSTATION))
- ret = KRB5KDC_ERR_POLICY;
- else
- ret = KRB5KDC_ERR_POLICY;
-
- samba_kdc_build_edata_reply(tmp_ctx, e_data, nt_status);
+ client_name, true, password_change);
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ /* Now do the standard Heimdal check */
+ ret = kdc_check_flags(context, config,
+ client_ex, client_name,
+ server_ex, server_name,
+ req->msg_type == krb_as_req);
+ } else {
+ if (NT_STATUS_EQUAL(nt_status, NT_STATUS_PASSWORD_MUST_CHANGE))
+ ret = KRB5KDC_ERR_KEY_EXPIRED;
+ else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_PASSWORD_EXPIRED))
+ ret = KRB5KDC_ERR_KEY_EXPIRED;
+ else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCOUNT_EXPIRED))
+ ret = KRB5KDC_ERR_CLIENT_REVOKED;
+ else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCOUNT_DISABLED))
+ ret = KRB5KDC_ERR_CLIENT_REVOKED;
+ else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_INVALID_LOGON_HOURS))
+ ret = KRB5KDC_ERR_CLIENT_REVOKED;
+ else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCOUNT_LOCKED_OUT))
+ ret = KRB5KDC_ERR_CLIENT_REVOKED;
+ else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_INVALID_WORKSTATION))
+ ret = KRB5KDC_ERR_POLICY;
+ else
+ ret = KRB5KDC_ERR_POLICY;
+
+ samba_kdc_build_edata_reply(tmp_ctx, e_data, nt_status);
+ }
return ret;
}