Patch password history for samba-3.0.5pre1, ldap backend
Jianliang Lu
j.lu at tiesse.com
Wed Jun 9 09:44:35 GMT 2004
I ported the patch on 3.0.5pre1, the reason of this post is to complete the
password policy, that was partially integrated on Samba. We are using this
policy in production for about 2 years, and we'd like that the samba.schema
will not be changed frequently. In this patch I've fixed also the
bad_password_count update problem.
Jianliang Lu
TieSse s.p.a. Ivrea (To) - Italy
j.lu at tiesse.com luj at libero.it
http://www.tiesse.com
--------------------- Patch --------------------------
--- samba-3.0.5pre1/source/smbd/chgpasswd.c.orig Wed Jun 9 09:23:20
2004
+++ samba-3.0.5pre1/source/smbd/chgpasswd.c Wed Jun 9 09:23:20 2004
@@ -933,6 +934,47 @@
}
/***********************************************************
+ This routine takes the given password and checks it against
+ the password history.
+************************************************************/
+static BOOL check_passwd_history(SAM_ACCOUNT *sampass, const char *plaintext)
+{
+ uchar new_lanman_p16[16];
+ uchar new_nt_p16[16];
+ uint8 *nt_pw;
+ uint8 *pwhistory;
+ BOOL found = False;
+ int i, pwHisLen;
+
+ account_policy_get(AP_PASSWORD_HISTORY, &pwHisLen);
+ if (pwHisLen == 0)
+ return False;
+
+
+ pwhistory = (uint8 *) pdb_get_pw_history (sampass);
+
+ if (!pwhistory)
+ return False;
+
+ nt_pw = (uint8*) pdb_get_nt_passwd(sampass);
+
+ E_md4hash(plaintext, new_nt_p16);
+
+ if (!memcmp(nt_pw, new_nt_p16, 16))
+ return True;
+
+ dump_data(10, new_nt_p16, 16);
+ dump_data(10, pwhistory, 16*pwHisLen);
+ for (i=0; i<pwHisLen; i++){
+ if (!memcmp((uint8 *) (pwhistory + i*16), new_nt_p16, 16))
+ found = True;
+ }
+
+ return found;
+}
+
+/***********************************************************
+/***********************************************************
Code to change the oem password. Changes both the lanman
and NT hashes. Old_passwd is almost always NULL.
NOTE this function is designed to be called as root. Check the old password
@@ -946,10 +988,10 @@
BOOL ret;
uint32 min_len;
- if (time(NULL) < pdb_get_pass_can_change_time(hnd)) {
+ if ((time(NULL) < pdb_get_pass_can_change_time(hnd)) &&
(pdb_get_pass_must_change_time(hnd) != 0)) {
DEBUG(1, ("user %s cannot change password now, must wait
until %s\n",
pdb_get_username(hnd), http_timestring
(pdb_get_pass_can_change_time(hnd))));
- return NT_STATUS_PASSWORD_RESTRICTION;
+ return NT_STATUS_ACCOUNT_RESTRICTION;
}
if (account_policy_get(AP_MIN_PASSWORD_LEN, &min_len) && (strlen
(new_passwd) < min_len)) {
@@ -970,6 +1012,9 @@
/* return NT_STATUS_PWD_TOO_SHORT; */
}
+ if (check_passwd_history(hnd,new_passwd))
+ return NT_STATUS_PASSWORD_RESTRICTION;
+
pass = Get_Pwnam(pdb_get_username(hnd));
if (!pass) {
DEBUG(1, ("check_oem_password: Username does not exist in
system !?!\n"));
--- samba-3.0.5pre1/source/include/passdb.h.orig Wed Jun 9 09:23:20
2004
+++ samba-3.0.5pre1/source/include/passdb.h Wed Jun 9 15:11:32 2004
@@ -99,6 +99,7 @@
PDB_UNKNOWN6,
PDB_LMPASSWD,
PDB_NTPASSWD,
+ PDB_PWHISTORY,
PDB_BACKEND_PRIVATE_DATA,
/* this must be the last element */
@@ -169,6 +170,7 @@
const char * workstations; /* login from workstations string
*/
const char * unknown_str ; /* don't know what this is, yet. */
const char * munged_dial ; /* munged path name and dial-back
tel number */
+ const uint8 * nt_pw_his; /* nt hashed password history */
DOM_SID user_sid; /* Primary User SID */
DOM_SID group_sid; /* Primary Group SID */
--- samba-3.0.5pre1/source/passdb/pdb_get_set.c.orig Wed Jun 9 09:23:20
2004
+++ samba-3.0.5pre1/source/passdb/pdb_get_set.c Wed Jun 9 15:12:21 2004
@@ -150,6 +150,15 @@
return (NULL);
}
+const uint8* pdb_get_pw_history (const SAM_ACCOUNT *sampass)
+{
+ if (sampass) {
+ return ((uint8*)sampass->private.nt_pw_his);
+ }
+ else
+ return (NULL);
+}
+
/* Return the plaintext password if known. Most of the time
it isn't, so don't assume anything magic about this function.
@@ -982,6 +991,31 @@
}
/*********************************************************************
+ Set the user's password history hash.
+ ********************************************************************/
+
+BOOL pdb_set_pw_history (SAM_ACCOUNT *sampass, const uint8 * pwd, int
historyLen, enum pdb_value_state flag)
+{
+ if (!sampass)
+ return False;
+
+ if (historyLen > 0){
+ if (pwd){
+ sampass->private.nt_pw_his = (uint8 *) talloc_memdup
(sampass->mem_ctx, pwd, historyLen*16);
+
+ if (!sampass->private.nt_pw_his) {
+ DEBUG(0, ("pdb_set_pw_his: talloc_memdup()
failed!\n"));
+ return False;
+ }
+ } else {
+ sampass->private.nt_pw_his = NULL;
+ }
+ }
+
+ return pdb_set_init_flags(sampass, PDB_PWHISTORY, flag);
+}
+
+/*********************************************************************
Set the user's plaintext password only (base procedure, see helper
below)
********************************************************************/
@@ -1135,10 +1169,18 @@
{
uchar new_lanman_p16[16];
uchar new_nt_p16[16];
+ uchar *pwhistory;
+ uchar nt_pw[16];
+ uchar * ntpw;
+ int pwHistLen;
if (!sampass || !plaintext)
return False;
+ ntpw = (uint8 *)pdb_get_nt_passwd(sampass);
+ if (ntpw)
+ memcpy ((uchar *)nt_pw, (uchar *)ntpw, 16);
+
/* Calculate the MD4 hash (NT compatible) of the password */
E_md4hash(plaintext, new_nt_p16);
@@ -1164,6 +1206,20 @@
if (!pdb_set_pass_changed_now (sampass))
return False;
+ if (pdb_get_acct_ctrl(sampass) & ACB_NORMAL) {
+ account_policy_get(AP_PASSWORD_HISTORY, &pwHistLen);
+ if (pwHistLen > 0){
+ pwhistory = (uint8 *)pdb_get_pw_history (sampass);
+ if (pwhistory){
+ memmove((uchar *) (pwhistory + 16),
pwhistory, (pwHistLen -1)*16 );
+ memcpy((uchar *)pwhistory, (uchar *)nt_pw,
16);
+ pdb_set_pw_history (sampass, pwhistory,
pwHistLen, PDB_CHANGED);
+ }
+ else {
+ DEBUG (100,("pdb_get_set.c:
pdb_set_plaintext_passwd: pwhistory was NULL!\n"));
+ }
+ }
+ }
return True;
}
--- samba-3.0.5pre1/examples/LDAP/samba.schema.orig Wed Jun 9 15:03:03
2004
+++ samba-3.0.5pre1/examples/LDAP/samba.schema Wed Jun 9 15:14:07 2004
@@ -212,7 +212,6 @@
EQUALITY integerMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
-
##
## string settings
##
@@ -251,6 +250,10 @@
EQUALITY caseExactMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{1050} )
+attributetype ( 1.3.6.1.4.1.7165.2.1.50 NAME 'sambaPasswordHistory'
+ DESC 'MD4 hash of the unicode password'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{32} )
##
## SID, of any type
##
@@ -329,7 +332,7 @@
displayName $ sambaHomePath $ sambaHomeDrive $
sambaLogonScript $
sambaProfilePath $ description $ sambaUserWorkstations $
sambaPrimaryGroupSID $ sambaDomainName $ sambaMungedDial $
- sambaBadPasswordCount $ sambaBadPasswordTime))
+ sambaBadPasswordCount $ sambaBadPasswordTime $
sambaPasswordHistory))
##
## Group mapping info
--- samba-3.0.5pre1/source/lib/smbldap.c.orig Wed Jun 9 09:23:20 2004
+++ samba-3.0.5pre1/source/lib/smbldap.c Wed Jun 9 15:14:55 2004
@@ -100,6 +100,8 @@
{ LDAP_ATTR_MUNGED_DIAL, "sambaMungedDial" },
{ LDAP_ATTR_BAD_PASSWORD_COUNT, "sambaBadPasswordCount" },
{ LDAP_ATTR_BAD_PASSWORD_TIME, "sambaBadPasswordTime" },
+ { LDAP_ATTR_PWD_HISTORY, "sambaPasswordHistory" },
+ { LDAP_ATTR_MOD_TIMESTAMP, "modifyTimestamp" },
{ LDAP_ATTR_LIST_END, NULL }
};
@@ -330,6 +332,45 @@
sizeof(pstring));
}
+
+/*******************************************************************
+ Search an attribute and return the multivalues found.
+******************************************************************/
+
+BOOL smbldap_get_single_attribute_multivalue (LDAP * ldap_struct,
LDAPMessage * entry,
+ const char *attribute, char *value, int
len, int * count)
+{
+ char **values;
+ int i;
+
+ if ( !attribute )
+ return False;
+
+ value[0] = '\0';
+
+ if ((values = ldap_get_values (ldap_struct, entry, attribute)) ==
NULL) {
+ DEBUG (10, ("smbldap_get_single_attribute_multivalue: [%s] =
[<does not exist>]\n", attribute));
+
+ return False;
+ }
+
+ for (i = 0; (values[i] != NULL) && (i < len); i++){
+ if (convert_string(CH_UTF8, CH_UNIX,values[i], -1,(value +
i*sizeof(pstring)), sizeof(pstring), False) == (size_t)-1) {
+ DEBUG(1, ("smbldap_get_single_attribute_multivalue:
string conversion of [%s] = [%s] failed!\n",
+ attribute, values[i]));
+ ldap_value_free(values);
+ return False;
+ }
+ }
+
+ *count = i;
+ ldap_value_free(values);
+#ifdef DEBUG_PASSWORDS
+ DEBUG (100, ("smbldap_get_single_attribute: [%s] = [%s]\n",
attribute, value));
+#endif
+ return True;
+}
+
/************************************************************************
Routine to manage the LDAPMod structure array
manage memory used by the array, by each struct, and values
--- samba-3.0.5pre1/source/include/smbldap.h.orig Wed Jun 9 09:23:20
2004
+++ samba-3.0.5pre1/source/include/smbldap.h Wed Jun 9 15:16:22 2004
@@ -93,8 +93,9 @@
#define LDAP_ATTR_LOGON_COUNT 36
#define LDAP_ATTR_MUNGED_DIAL 37
#define LDAP_ATTR_BAD_PASSWORD_TIME 38
-
+#define LDAP_ATTR_PWD_HISTORY 39
#define LDAP_ATTR_SID_LIST 40
+#define LDAP_ATTR_MOD_TIMESTAMP 41
typedef struct _attrib_map_entry {
int attrib;
--- samba-3.0.5pre1/source/passdb/pdb_ldap.c.orig Wed Jun 9 09:23:20
2004
+++ samba-3.0.5pre1/source/passdb/pdb_ldap.c Wed Jun 9 15:13:16 2004
@@ -81,7 +81,6 @@
#define SAM_ACCOUNT struct sam_passwd
#endif
-#define MODIFY_TIMESTAMP_STRING "modifyTimestamp"
#include "smbldap.h"
@@ -301,7 +300,8 @@
really exist. */
for (attrib = attrs; *attrib != NULL; attrib++) {
- if (StrCaseCmp(*attrib, name) == 0) {
+ if ((StrCaseCmp(*attrib, name) == 0) &&
+ !(StrCaseCmp(*attrib, get_userattr_key2string
(ldap_state->schema_ver, LDAP_ATTR_MOD_TIMESTAMP)))) {
DEBUG(10, ("ldapsam_delete_entry: deleting
attribute %s\n", name));
smbldap_set_mod(&mods, LDAP_MOD_DELETE, name,
NULL);
}
@@ -400,8 +400,11 @@
pstring temp;
struct tm tm;
- if (!smbldap_get_single_pstring(ldap_state->smbldap_state-
>ldap_struct,
- entry, MODIFY_TIMESTAMP_STRING,
temp))
+ 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);
@@ -448,6 +451,9 @@
uint8 hours[MAX_HOURS_LEN];
pstring temp;
LOGIN_CACHE *cache_entry = NULL;
+ uint8 *pwhist;
+ int i, pwcount, pwHistLen;
+ char *temp1;
/*
* do a little initialization
@@ -694,6 +700,37 @@
ZERO_STRUCT(smbntpwd);
}
+
+ account_policy_get(AP_PASSWORD_HISTORY, &pwHistLen);
+ if (pwHistLen > 0){
+ if ((temp1 = (char *) malloc(sizeof(pstring) * pwHistLen))
== NULL) {
+ DEBUG(0, ("pdb_ldap.c: init_sam_from_ldap: malloc
failed!\n"));
+ return False;
+ }
+ memset(temp1, 0, sizeof(pstring)*pwHistLen);
+ if ((pwhist = (uint8 *) malloc(16 * pwHistLen)) == NULL){
+ DEBUG(0, ("pdb_ldap.c: init_sam_from_ldap: malloc
failed!\n"));
+ return False;
+ }
+ memset((uint8 *) pwhist, 0, 16*pwHistLen);
+ if (!smbldap_get_single_attribute_multivalue (ldap_state-
>smbldap_state->ldap_struct, entry, get_userattr_key2string(ldap_state-
>schema_ver, LDAP_ATTR_PWD_HISTORY), temp1, pwHistLen, &pwcount)) {
+ /* leave as default */
+ } else {
+ for (i = 0; (i < pwHistLen) && (i < pwcount); i++){
+ pdb_gethexpwd((char *)(temp1 + i*sizeof
(pstring)), smbntpwd);
+ memcpy((unsigned char *) (pwhist + i*16),
smbntpwd, 16);
+ ZERO_STRUCT(smbntpwd);
+ }
+ }
+ if (!pdb_set_pw_history(sampass, pwhist, pwHistLen, PDB_SET)){
+ SAFE_FREE (temp1);
+ SAFE_FREE (pwhist);
+ return False;
+ }
+ SAFE_FREE (temp1);
+ SAFE_FREE (pwhist);
+ }
+
if (!smbldap_get_single_pstring (ldap_state->smbldap_state-
>ldap_struct, entry,
get_userattr_key2string(ldap_state->schema_ver,
LDAP_ATTR_ACB_INFO), temp)) {
acct_ctrl |= ACB_NORMAL;
@@ -793,6 +830,8 @@
{
pstring temp;
uint32 rid;
+ uint8 * temp1;
+ int pwHistLen, i;
if (mods == NULL || sampass == NULL) {
DEBUG(0, ("init_ldap_from_sam: NULL parameters found!\n"));
@@ -985,6 +1024,26 @@
}
}
+
+ if (need_update(sampass, PDB_PWHISTORY)) {
+ account_policy_get(AP_PASSWORD_HISTORY, &pwHistLen);
+ temp1 = (uint8 *) pdb_get_pw_history(sampass);
+ if (temp1 != NULL)
+ for (i=0; i< pwHistLen; i++){
+ pdb_sethexpwd (temp, (uint8 *)(temp1
+ i*16), 0);
+ DEBUG(100, ("temp1=%s, temp=%s\n",
temp1, temp));
+ if (!strncmp(temp,"0000000000",10))
+ break;
+ smbldap_set_mod
(mods,LDAP_MOD_REPLACE, get_userattr_key2string(ldap_state-
>schema_ver,LDAP_ATTR_PWD_HISTORY),temp);
+ /*
+ smbldap_make_mod(ldap_state-
>smbldap_state->ldap_struct, existing,
+ mods,
get_userattr_key2string(ldap_state->schema_ver,
+
LDAP_ATTR_PWD_HISTORY),
+
temp);
+
*/
+ }
+ }
+
if (need_update(sampass, PDB_PASSLASTSET)) {
slprintf (temp, sizeof (temp) - 1, "%li",
pdb_get_pass_last_set_time(sampass));
smbldap_make_mod(ldap_state->smbldap_state-
>ldap_struct, existing, mods,
More information about the samba-technical
mailing list