summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJim McDonough <jmcd@samba.org>2004-03-18 20:05:00 +0000
committerJim McDonough <jmcd@samba.org>2004-03-18 20:05:00 +0000
commit2332bebd9fabc32dbc2756347338dbecc06e527b (patch)
tree08d49649a4e52c75765b6e915e7c204d3bd69469
parent4b9423fc2205b6f37300e134f425d0aef3022a6e (diff)
downloadsamba-2332bebd9fabc32dbc2756347338dbecc06e527b.tar.gz
samba-2332bebd9fabc32dbc2756347338dbecc06e527b.tar.bz2
samba-2332bebd9fabc32dbc2756347338dbecc06e527b.zip
merge from 3.0...LDAP password lockout support
(This used to be commit b627cee3848d73e35181c9e6fdd9931452b28e48)
-rw-r--r--source3/Makefile.in3
-rw-r--r--source3/include/passdb.h9
-rw-r--r--source3/include/smbldap.h1
-rw-r--r--source3/lib/smbldap.c1
-rw-r--r--source3/passdb/login_cache.c174
-rw-r--r--source3/passdb/pdb_ldap.c121
6 files changed, 307 insertions, 2 deletions
diff --git a/source3/Makefile.in b/source3/Makefile.in
index def96051b7..9b4c8d5c04 100644
--- a/source3/Makefile.in
+++ b/source3/Makefile.in
@@ -297,7 +297,8 @@ PASSDB_GET_SET_OBJ = passdb/pdb_get_set.o
PASSDB_OBJ = $(PASSDB_GET_SET_OBJ) passdb/passdb.o passdb/pdb_interface.o \
passdb/util_sam_sid.o passdb/pdb_compat.o \
- passdb/privileges.o passdb/lookup_sid.o @PDB_STATIC@ passdb/pdb_sql.o
+ passdb/privileges.o passdb/lookup_sid.o \
+ passdb/login_cache.o @PDB_STATIC@ passdb/pdb_sql.o
XML_OBJ = passdb/pdb_xml.o
MYSQL_OBJ = passdb/pdb_mysql.o
diff --git a/source3/include/passdb.h b/source3/include/passdb.h
index 21feb7208f..92e4bf3e8d 100644
--- a/source3/include/passdb.h
+++ b/source3/include/passdb.h
@@ -134,6 +134,15 @@ enum pdb_value_state {
#define IS_SAM_SET(x, flag) (pdb_get_init_flags(x, flag) == PDB_SET)
#define IS_SAM_CHANGED(x, flag) (pdb_get_init_flags(x, flag) == PDB_CHANGED)
#define IS_SAM_DEFAULT(x, flag) (pdb_get_init_flags(x, flag) == PDB_DEFAULT)
+
+/* cache for bad password lockout data, to be used on replicated SAMs */
+typedef struct logon_cache_struct
+{
+ time_t entry_timestamp;
+ uint16 acct_ctrl;
+ uint16 bad_password_count;
+ time_t bad_password_time;
+} LOGIN_CACHE;
typedef struct sam_passwd
{
diff --git a/source3/include/smbldap.h b/source3/include/smbldap.h
index 2f71f971d9..68a2c00afe 100644
--- a/source3/include/smbldap.h
+++ b/source3/include/smbldap.h
@@ -92,6 +92,7 @@
#define LDAP_ATTR_LOGON_COUNT 36
#define LDAP_ATTR_MUNGED_DIAL 37
#define LDAP_ATTR_BAD_PASSWORD_TIME 38
+#define LDAP_ATTR_MOD_TIMESTAMP 39
typedef struct _attrib_map_entry {
int attrib;
diff --git a/source3/lib/smbldap.c b/source3/lib/smbldap.c
index c15ba51306..20c2163cde 100644
--- a/source3/lib/smbldap.c
+++ b/source3/lib/smbldap.c
@@ -100,6 +100,7 @@ ATTRIB_MAP_ENTRY attrib_map_v30[] = {
{ LDAP_ATTR_MUNGED_DIAL, "sambaMungedDial" },
{ LDAP_ATTR_BAD_PASSWORD_COUNT, "sambaBadPasswordCount" },
{ LDAP_ATTR_BAD_PASSWORD_TIME, "sambaBadPasswordTime" },
+ { LDAP_ATTR_MOD_TIMESTAMP, "modifyTimestamp" },
{ LDAP_ATTR_LIST_END, NULL }
};
diff --git a/source3/passdb/login_cache.c b/source3/passdb/login_cache.c
new file mode 100644
index 0000000000..4b760172ec
--- /dev/null
+++ b/source3/passdb/login_cache.c
@@ -0,0 +1,174 @@
+/*
+ Unix SMB/CIFS implementation.
+ SAM_ACCOUNT local cache for
+ Copyright (C) Jim McDonough (jmcd@us.ibm.com) 2004.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_PASSDB
+
+#define LOGIN_CACHE_FILE "login_cache.tdb"
+
+#define SAM_CACHE_FORMAT "dwwd"
+
+static TDB_CONTEXT *cache;
+
+BOOL login_cache_init(void)
+{
+ char* cache_fname = NULL;
+
+ /* skip file open if it's already opened */
+ if (cache) return True;
+
+ asprintf(&cache_fname, "%s/%s", lp_lockdir(), LOGIN_CACHE_FILE);
+ if (cache_fname)
+ DEBUG(5, ("Opening cache file at %s\n", cache_fname));
+ else {
+ DEBUG(0, ("Filename allocation failed.\n"));
+ return False;
+ }
+
+ cache = tdb_open_log(cache_fname, 0, TDB_DEFAULT,
+ O_RDWR|O_CREAT, 0644);
+
+ if (!cache)
+ DEBUG(5, ("Attempt to open %s failed.\n", cache_fname));
+
+ SAFE_FREE(cache_fname);
+
+ return (cache ? True : False);
+}
+
+BOOL login_cache_shutdown(void)
+{
+ /* tdb_close routine returns -1 on error */
+ if (!cache) return False;
+ DEBUG(5, ("Closing cache file\n"));
+ return tdb_close(cache) != -1;
+}
+
+/* if we can't read the cache, oh well, no need to return anything */
+LOGIN_CACHE * login_cache_read(SAM_ACCOUNT *sampass)
+{
+ TDB_DATA keybuf, databuf;
+ LOGIN_CACHE *entry;
+
+ if (!login_cache_init())
+ return NULL;
+
+ keybuf.dptr = strdup(pdb_get_nt_username(sampass));
+ if (!keybuf.dptr || !strlen(keybuf.dptr)) {
+ SAFE_FREE(keybuf.dptr);
+ return NULL;
+ }
+ keybuf.dsize = strlen(keybuf.dptr) + 1;
+
+ DEBUG(7, ("Looking up login cache for user %s\n",
+ keybuf.dptr));
+ databuf = tdb_fetch(cache, keybuf);
+ SAFE_FREE(keybuf.dptr);
+
+ if (!(entry = malloc(sizeof(LOGIN_CACHE)))) {
+ DEBUG(1, ("Unable to allocate cache entry buffer!\n"));
+ SAFE_FREE(databuf.dptr);
+ return NULL;
+ }
+
+ if (tdb_unpack (databuf.dptr, databuf.dsize, SAM_CACHE_FORMAT,
+ &entry->entry_timestamp, &entry->acct_ctrl,
+ &entry->bad_password_count,
+ &entry->bad_password_time) == -1) {
+ DEBUG(7, ("No cache entry found\n"));
+ SAFE_FREE(databuf.dptr);
+ return NULL;
+ }
+
+ DEBUG(5, ("Found login cache entry: timestamp %12u, flags 0x%x, count %d, time %12u\n",
+ entry->entry_timestamp, entry->acct_ctrl,
+ entry->bad_password_count, entry->bad_password_time));
+ return entry;
+}
+
+BOOL login_cache_write(const SAM_ACCOUNT *sampass, LOGIN_CACHE entry)
+{
+
+ TDB_DATA keybuf, databuf;
+ BOOL ret;
+
+
+ keybuf.dptr = strdup(pdb_get_nt_username(sampass));
+ if (!keybuf.dptr || !strlen(keybuf.dptr)) {
+ SAFE_FREE(keybuf.dptr);
+ return False;
+ }
+ keybuf.dsize = strlen(keybuf.dptr) + 1;
+
+ entry.entry_timestamp = time(NULL);
+
+ databuf.dsize =
+ tdb_pack(NULL, 0, SAM_CACHE_FORMAT,
+ entry.entry_timestamp,
+ entry.acct_ctrl,
+ entry.bad_password_count,
+ entry.bad_password_time);
+ databuf.dptr = malloc(databuf.dsize);
+ if (!databuf.dptr) {
+ SAFE_FREE(keybuf.dptr);
+ return False;
+ }
+
+ if (tdb_pack(databuf.dptr, databuf.dsize, SAM_CACHE_FORMAT,
+ entry.entry_timestamp,
+ entry.acct_ctrl,
+ entry.bad_password_count,
+ entry.bad_password_time)
+ != databuf.dsize) {
+ SAFE_FREE(keybuf.dptr);
+ SAFE_FREE(databuf.dptr);
+ return False;
+ }
+
+ ret = tdb_store(cache, keybuf, databuf, 0);
+ SAFE_FREE(keybuf.dptr);
+ SAFE_FREE(databuf.dptr);
+ return ret == 0;
+}
+
+BOOL login_cache_delentry(const SAM_ACCOUNT *sampass)
+{
+ int ret;
+ TDB_DATA keybuf;
+
+ if (!login_cache_init())
+ return False;
+
+ keybuf.dptr = strdup(pdb_get_nt_username(sampass));
+ if (!keybuf.dptr || !strlen(keybuf.dptr)) {
+ SAFE_FREE(keybuf.dptr);
+ return False;
+ }
+ keybuf.dsize = strlen(keybuf.dptr) + 1;
+ DEBUG(9, ("About to delete entry for %s\n", keybuf.dptr));
+ ret = tdb_delete(cache, keybuf);
+ DEBUG(9, ("tdb_delete returned %d\n", ret));
+
+ SAFE_FREE(keybuf.dptr);
+ return ret == 0;
+}
+
diff --git a/source3/passdb/pdb_ldap.c b/source3/passdb/pdb_ldap.c
index 0ebb63b3fb..b7e28a9e99 100644
--- a/source3/passdb/pdb_ldap.c
+++ b/source3/passdb/pdb_ldap.c
@@ -391,6 +391,25 @@ static BOOL get_unix_attributes (struct ldapsam_privates *ldap_state,
#endif
+static time_t ldapsam_get_entry_timestamp(
+ struct ldapsam_privates *ldap_state,
+ LDAPMessage * entry)
+{
+ pstring temp;
+ struct tm tm;
+
+ if (!smbldap_get_single_pstring(
+ ldap_state->smbldap_state->ldap_struct, entry,
+ get_userattr_key2string(ldap_state->schema_ver,
+ LDAP_ATTR_MOD_TIMESTAMP),
+ temp))
+ return (time_t) 0;
+
+ strptime(temp, "%Y%m%d%H%M%SZ", &tm);
+ tzset();
+ return (mktime(&tm) - timezone);
+}
+
/**********************************************************************
Initialize SAM_ACCOUNT from an LDAP query.
(Based on init_sam_from_buffer in pdb_tdb.c)
@@ -405,7 +424,9 @@ static BOOL init_sam_from_ldap (struct ldapsam_privates *ldap_state,
kickoff_time,
pass_last_set_time,
pass_can_change_time,
- pass_must_change_time;
+ pass_must_change_time,
+ ldap_entry_time,
+ bad_password_time;
pstring username,
domain,
nt_username,
@@ -427,6 +448,7 @@ static BOOL init_sam_from_ldap (struct ldapsam_privates *ldap_state,
uint32 hours_len;
uint8 hours[MAX_HOURS_LEN];
pstring temp;
+ LOGIN_CACHE *cache_entry = NULL;
/*
* do a little initialization
@@ -720,6 +742,15 @@ static BOOL init_sam_from_ldap (struct ldapsam_privates *ldap_state,
pdb_set_bad_password_count(sampass, bad_password_count, PDB_SET);
}
+ if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_BAD_PASSWORD_TIME), temp)) {
+ /* leave as default */
+ } else {
+ bad_password_time = (time_t) atol(temp);
+ pdb_set_bad_password_time(sampass, bad_password_time, PDB_SET);
+ }
+
+
if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_LOGON_COUNT), temp)) {
/* leave as default */
@@ -732,6 +763,43 @@ static BOOL init_sam_from_ldap (struct ldapsam_privates *ldap_state,
pdb_set_hours(sampass, hours, PDB_SET);
+ /* check the timestamp of the cache vs ldap entry */
+ if (!(ldap_entry_time = ldapsam_get_entry_timestamp(ldap_state,
+ entry)))
+ return True;
+
+ /* see if we have newer updates */
+ if (!(cache_entry = login_cache_read(sampass))) {
+ DEBUG (9, ("No cache entry, bad count = %d, bad time = %d\n",
+ pdb_get_bad_password_count(sampass),
+ pdb_get_bad_password_time(sampass)));
+ return True;
+ }
+
+ DEBUG(7, ("ldap time is %d, cache time is %d, bad time = %d\n",
+ ldap_entry_time, cache_entry->entry_timestamp,
+ cache_entry->bad_password_time));
+
+ if (ldap_entry_time > cache_entry->entry_timestamp) {
+ /* cache is older than directory , so
+ we need to delete the entry but allow the
+ fields to be written out */
+ login_cache_delentry(sampass);
+ } else {
+ /* read cache in */
+ pdb_set_acct_ctrl(sampass,
+ pdb_get_acct_ctrl(sampass) |
+ (cache_entry->acct_ctrl & ACB_AUTOLOCK),
+ PDB_SET);
+ pdb_set_bad_password_count(sampass,
+ cache_entry->bad_password_count,
+ PDB_SET);
+ pdb_set_bad_password_time(sampass,
+ cache_entry->bad_password_time,
+ PDB_SET);
+ }
+
+ SAFE_FREE(cache_entry);
return True;
}
@@ -907,6 +975,7 @@ static BOOL init_ldap_from_sam (struct ldapsam_privates *ldap_state,
smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_PWD_MUST_CHANGE), temp);
+
if ((pdb_get_acct_ctrl(sampass)&(ACB_WSTRUST|ACB_SVRTRUST|ACB_DOMTRUST))
|| (lp_ldap_passwd_sync()!=LDAP_PASSWD_SYNC_ONLY)) {
@@ -954,6 +1023,56 @@ static BOOL init_ldap_from_sam (struct ldapsam_privates *ldap_state,
get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_ACB_INFO),
pdb_encode_acct_ctrl (pdb_get_acct_ctrl(sampass), NEW_PW_FORMAT_SPACE_PADDED_LEN));
+ /* password lockout cache:
+ - If we are now autolocking or clearing, we write to ldap
+ - If we are clearing, we delete the cache entry
+ - If the count is > 0, we update the cache
+
+ This even means when autolocking, we cache, just in case the
+ update doesn't work, and we have to cache the autolock flag */
+
+ if (need_update(sampass, PDB_BAD_PASSWORD_COUNT)) /* &&
+ need_update(sampass, PDB_BAD_PASSWORD_TIME)) */ {
+ uint16 badcount = pdb_get_bad_password_count(sampass);
+ time_t badtime = pdb_get_bad_password_time(sampass);
+ uint32 pol;
+ account_policy_get(AP_BAD_ATTEMPT_LOCKOUT, &pol);
+
+ DEBUG(3, ("updating bad password fields, policy=%d, count=%d, time=%d\n", pol, badcount, badtime));
+
+ if ((badcount >= pol) || (badcount == 0)) {
+ DEBUG(7, ("making mods to update ldap, count=%d, time=%d\n", badcount, badtime));
+ slprintf (temp, sizeof (temp) - 1, "%li", badcount);
+ smbldap_make_mod(
+ ldap_state->smbldap_state->ldap_struct,
+ existing, mods,
+ get_userattr_key2string(
+ ldap_state->schema_ver,
+ LDAP_ATTR_BAD_PASSWORD_COUNT),
+ temp);
+
+ slprintf (temp, sizeof (temp) - 1, "%li", badtime);
+ smbldap_make_mod(
+ ldap_state->smbldap_state->ldap_struct,
+ existing, mods,
+ get_userattr_key2string(
+ ldap_state->schema_ver,
+ LDAP_ATTR_BAD_PASSWORD_TIME),
+ temp);
+ }
+ if (badcount == 0) {
+ DEBUG(7, ("bad password count is reset, deleting login cache entry for %s\n", pdb_get_nt_username(sampass)));
+ login_cache_delentry(sampass);
+ } else {
+ LOGIN_CACHE cache_entry ={time(NULL),
+ pdb_get_acct_ctrl(sampass),
+ badcount, badtime};
+ DEBUG(7, ("Updating bad password count and time in login cache\n"));
+ login_cache_write(sampass, cache_entry);
+ }
+
+ }
+
return True;
}