r3628: A typo and a compile-warning.
[ira/wip.git] / source3 / passdb / pdb_ldap.c
index 5cf1691f0dd0c1ddeeda08b89d3bfd939af098e9..41d650fad5260ef081b08188d3082643c616c757 100644 (file)
@@ -5,7 +5,7 @@
    Copyright (C) Gerald Carter                 2001-2003
    Copyright (C) Shahms King                   2001
    Copyright (C) Andrew Bartlett               2002-2003
-   Copyright (C) Stefan (metze) Metzmacher     2002
+   Copyright (C) Stefan (metze) Metzmacher     2002-2003
     
    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
@@ -148,6 +148,27 @@ static char** get_userattr_list( int schema_ver )
        return NULL;
 }
 
+/**************************************************************************
+ Return the list of attribute names to delete given a user schema version.
+**************************************************************************/
+
+static char** get_userattr_delete_list( int schema_ver )
+{
+       switch ( schema_ver ) {
+               case SCHEMAVER_SAMBAACCOUNT:
+                       return get_attr_list( attrib_map_to_delete_v22 );
+                       
+               case SCHEMAVER_SAMBASAMACCOUNT:
+                       return get_attr_list( attrib_map_to_delete_v30 );
+               default:
+                       DEBUG(0,("get_userattr_delete_list: unknown schema version specified!\n"));
+                       break;
+       }
+       
+       return NULL;
+}
+
+
 /*******************************************************************
  Generate the LDAP search filter for the objectclass based on the 
  version of the schema we are using.
@@ -258,7 +279,7 @@ static NTSTATUS ldapsam_delete_entry(struct ldapsam_privates *ldap_state,
                                     char **attrs)
 {
        int rc;
-       LDAPMessage *entry;
+       LDAPMessage *entry = NULL;
        LDAPMod **mods = NULL;
        char *name, *dn;
        BerElement *ptr = NULL;
@@ -299,9 +320,16 @@ static NTSTATUS ldapsam_delete_entry(struct ldapsam_privates *ldap_state,
                   really exist. */
 
                for (attrib = attrs; *attrib != NULL; attrib++) {
-                       if (StrCaseCmp(*attrib, name) == 0) {
-                               DEBUG(10, ("ldapsam_delete_entry: deleting attribute %s\n", name));
-                               smbldap_set_mod(&mods, LDAP_MOD_DELETE, name, NULL);
+                       /* Don't delete LDAP_ATTR_MOD_TIMESTAMP attribute. */
+                       if (strequal(*attrib, get_userattr_key2string(ldap_state->schema_ver,
+                                               LDAP_ATTR_MOD_TIMESTAMP))) {
+                               continue;
+                       }
+                       if (strequal(*attrib, name)) {
+                               DEBUG(10, ("ldapsam_delete_entry: deleting "
+                                          "attribute %s\n", name));
+                               smbldap_set_mod(&mods, LDAP_MOD_DELETE, name,
+                                               NULL);
                        }
                }
 
@@ -356,7 +384,7 @@ static BOOL get_unix_attributes (struct ldapsam_privates *ldap_state,
        }
 
        for (values=ldap_values;*values;values++) {
-               if (strcasecmp(*values, LDAP_OBJ_POSIXACCOUNT ) == 0) {
+               if (strequal(*values, LDAP_OBJ_POSIXACCOUNT )) {
                        break;
                }
        }
@@ -368,13 +396,13 @@ static BOOL get_unix_attributes (struct ldapsam_privates *ldap_state,
        }
        ldap_value_free(ldap_values);
 
-       if ( !smbldap_get_single_attribute(ldap_state->smbldap_state->ldap_struct, entry, 
+       if ( !smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry, 
                get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_UNIX_HOME), homedir) ) 
        {
                return False;
        }
        
-       if ( !smbldap_get_single_attribute(ldap_state->smbldap_state->ldap_struct, entry, 
+       if ( !smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry, 
                get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_GIDNUMBER), temp) )
        {
                return False;
@@ -391,6 +419,23 @@ 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 timegm(&tm);
+}
+
 /**********************************************************************
  Initialize SAM_ACCOUNT from an LDAP query.
  (Based on init_sam_from_buffer in pdb_tdb.c)
@@ -405,7 +450,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,
@@ -415,8 +462,8 @@ static BOOL init_sam_from_ldap (struct ldapsam_privates *ldap_state,
                        logon_script,
                        profile_path,
                        acct_desc,
-                       munged_dial,
                        workstations;
+       char            munged_dial[2048];
        uint32          user_rid; 
        uint8           smblmpwd[LM_HASH_LEN],
                        smbntpwd[NT_HASH_LEN];
@@ -427,6 +474,8 @@ 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;
+       int pwHistLen;
 
        /*
         * do a little initialization
@@ -454,7 +503,7 @@ static BOOL init_sam_from_ldap (struct ldapsam_privates *ldap_state,
                return False;
        }
        
-       if (!smbldap_get_single_attribute(ldap_state->smbldap_state->ldap_struct, entry, "uid", username)) {
+       if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry, "uid", username)) {
                DEBUG(1, ("init_sam_from_ldap: No uid attribute found for this user!\n"));
                return False;
        }
@@ -473,25 +522,25 @@ static BOOL init_sam_from_ldap (struct ldapsam_privates *ldap_state,
        /* deal with different attributes between the schema first */
        
        if ( ldap_state->schema_ver == SCHEMAVER_SAMBASAMACCOUNT ) {
-               if (smbldap_get_single_attribute(ldap_state->smbldap_state->ldap_struct, entry, 
+               if (smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry, 
                                get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_USER_SID), temp)) {
                        pdb_set_user_sid_from_string(sampass, temp, PDB_SET);
                }
                
-               if (smbldap_get_single_attribute(ldap_state->smbldap_state->ldap_struct, entry, 
+               if (smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry, 
                                get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_PRIMARY_GROUP_SID), temp)) {
                        pdb_set_group_sid_from_string(sampass, temp, PDB_SET);                  
                } else {
                        pdb_set_group_sid_from_rid(sampass, DOMAIN_GROUP_RID_USERS, PDB_DEFAULT);
                }
        } else {
-               if (smbldap_get_single_attribute(ldap_state->smbldap_state->ldap_struct, entry,
+               if (smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
                                get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_USER_RID), temp)) {
                        user_rid = (uint32)atol(temp);
                        pdb_set_user_sid_from_rid(sampass, user_rid, PDB_SET);
                }
                
-               if (!smbldap_get_single_attribute(ldap_state->smbldap_state->ldap_struct, entry, 
+               if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry, 
                                get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_PRIMARY_GROUP_RID), temp)) {
                        pdb_set_group_sid_from_rid(sampass, DOMAIN_GROUP_RID_USERS, PDB_DEFAULT);
                } else {
@@ -517,29 +566,7 @@ static BOOL init_sam_from_ldap (struct ldapsam_privates *ldap_state,
                return False;
        }
 
-
-#if 0  /* JERRY -- not used anymore */
-       /* 
-        * If so configured, try and get the values from LDAP 
-        */
-
-       if (lp_ldap_trust_ids() && (get_unix_attributes(ldap_state, sampass, entry, &gid))) 
-       {       
-               if (pdb_get_init_flags(sampass,PDB_GROUPSID) == PDB_DEFAULT) 
-               {
-                       GROUP_MAP map;
-                       /* call the mapping code here */
-                       if(pdb_getgrgid(&map, gid)) {
-                               pdb_set_group_sid(sampass, &map.sid, PDB_SET);
-                       } 
-                       else {
-                               pdb_set_group_sid_from_rid(sampass, pdb_gid_to_group_rid(gid), PDB_SET);
-                       }
-               }
-       }
-#endif
-
-       if (!smbldap_get_single_attribute(ldap_state->smbldap_state->ldap_struct, entry, 
+       if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry, 
                        get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_PWD_LAST_SET), temp)) {
                /* leave as default */
        } else {
@@ -547,7 +574,7 @@ static BOOL init_sam_from_ldap (struct ldapsam_privates *ldap_state,
                pdb_set_pass_last_set_time(sampass, pass_last_set_time, PDB_SET);
        }
 
-       if (!smbldap_get_single_attribute(ldap_state->smbldap_state->ldap_struct, entry, 
+       if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry, 
                        get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_LOGON_TIME), temp)) {
                /* leave as default */
        } else {
@@ -555,7 +582,7 @@ static BOOL init_sam_from_ldap (struct ldapsam_privates *ldap_state,
                pdb_set_logon_time(sampass, logon_time, PDB_SET);
        }
 
-       if (!smbldap_get_single_attribute(ldap_state->smbldap_state->ldap_struct, entry, 
+       if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry, 
                        get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_LOGOFF_TIME), temp)) {
                /* leave as default */
        } else {
@@ -563,7 +590,7 @@ static BOOL init_sam_from_ldap (struct ldapsam_privates *ldap_state,
                pdb_set_logoff_time(sampass, logoff_time, PDB_SET);
        }
 
-       if (!smbldap_get_single_attribute(ldap_state->smbldap_state->ldap_struct, entry, 
+       if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry, 
                        get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_KICKOFF_TIME), temp)) {
                /* leave as default */
        } else {
@@ -571,7 +598,7 @@ static BOOL init_sam_from_ldap (struct ldapsam_privates *ldap_state,
                pdb_set_kickoff_time(sampass, kickoff_time, PDB_SET);
        }
 
-       if (!smbldap_get_single_attribute(ldap_state->smbldap_state->ldap_struct, entry, 
+       if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry, 
                        get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_PWD_CAN_CHANGE), temp)) {
                /* leave as default */
        } else {
@@ -579,7 +606,7 @@ static BOOL init_sam_from_ldap (struct ldapsam_privates *ldap_state,
                pdb_set_pass_can_change_time(sampass, pass_can_change_time, PDB_SET);
        }
 
-       if (!smbldap_get_single_attribute(ldap_state->smbldap_state->ldap_struct, entry, 
+       if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry, 
                        get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_PWD_MUST_CHANGE), temp)) {    
                /* leave as default */
        } else {
@@ -593,9 +620,9 @@ static BOOL init_sam_from_ldap (struct ldapsam_privates *ldap_state,
         * that fits your needs; using cn then displayName rather than 'userFullName'
         */
 
-       if (!smbldap_get_single_attribute(ldap_state->smbldap_state->ldap_struct, entry,
+       if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
                        get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_DISPLAY_NAME), fullname)) {
-               if (!smbldap_get_single_attribute(ldap_state->smbldap_state->ldap_struct, entry,
+               if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
                                get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_CN), fullname)) {
                        /* leave as default */
                } else {
@@ -605,7 +632,7 @@ static BOOL init_sam_from_ldap (struct ldapsam_privates *ldap_state,
                pdb_set_fullname(sampass, fullname, PDB_SET);
        }
 
-       if (!smbldap_get_single_attribute(ldap_state->smbldap_state->ldap_struct, entry, 
+       if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry, 
                        get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_HOME_DRIVE), dir_drive)) 
        {
                pdb_set_dir_drive( sampass, 
@@ -615,7 +642,7 @@ static BOOL init_sam_from_ldap (struct ldapsam_privates *ldap_state,
                pdb_set_dir_drive(sampass, dir_drive, PDB_SET);
        }
 
-       if (!smbldap_get_single_attribute(ldap_state->smbldap_state->ldap_struct, entry,
+       if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
                        get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_HOME_PATH), homedir)) 
        {
                pdb_set_homedir( sampass, 
@@ -625,7 +652,7 @@ static BOOL init_sam_from_ldap (struct ldapsam_privates *ldap_state,
                pdb_set_homedir(sampass, homedir, PDB_SET);
        }
 
-       if (!smbldap_get_single_attribute(ldap_state->smbldap_state->ldap_struct, entry,
+       if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
                        get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_LOGON_SCRIPT), logon_script)) 
        {
                pdb_set_logon_script( sampass, 
@@ -635,7 +662,7 @@ static BOOL init_sam_from_ldap (struct ldapsam_privates *ldap_state,
                pdb_set_logon_script(sampass, logon_script, PDB_SET);
        }
 
-       if (!smbldap_get_single_attribute(ldap_state->smbldap_state->ldap_struct, entry,
+       if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
                        get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_PROFILE_PATH), profile_path)) 
        {
                pdb_set_profile_path( sampass, 
@@ -645,7 +672,7 @@ static BOOL init_sam_from_ldap (struct ldapsam_privates *ldap_state,
                pdb_set_profile_path(sampass, profile_path, PDB_SET);
        }
 
-       if (!smbldap_get_single_attribute(ldap_state->smbldap_state->ldap_struct, entry, 
+       if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry, 
                get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_DESC), acct_desc)) 
        {
                /* leave as default */
@@ -653,20 +680,27 @@ static BOOL init_sam_from_ldap (struct ldapsam_privates *ldap_state,
                pdb_set_acct_desc(sampass, acct_desc, PDB_SET);
        }
 
-       if (!smbldap_get_single_attribute(ldap_state->smbldap_state->ldap_struct, entry, 
+       if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry, 
                get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_USER_WKS), workstations)) {
                /* leave as default */;
        } else {
                pdb_set_workstations(sampass, workstations, PDB_SET);
        }
 
+       if (!smbldap_get_single_attribute(ldap_state->smbldap_state->ldap_struct, entry, 
+               get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_MUNGED_DIAL), munged_dial, sizeof(munged_dial))) {
+               /* leave as default */;
+       } else {
+               pdb_set_munged_dial(sampass, munged_dial, PDB_SET);
+       }
+       
        /* FIXME: hours stuff should be cleaner */
        
        logon_divs = 168;
        hours_len = 21;
        memset(hours, 0xff, hours_len);
 
-       if (!smbldap_get_single_attribute (ldap_state->smbldap_state->ldap_struct, entry, 
+       if (!smbldap_get_single_pstring (ldap_state->smbldap_state->ldap_struct, entry, 
                get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_LMPW), temp)) {
                /* leave as default */
        } else {
@@ -677,7 +711,7 @@ static BOOL init_sam_from_ldap (struct ldapsam_privates *ldap_state,
                ZERO_STRUCT(smblmpwd);
        }
 
-       if (!smbldap_get_single_attribute (ldap_state->smbldap_state->ldap_struct, entry,
+       if (!smbldap_get_single_pstring (ldap_state->smbldap_state->ldap_struct, entry,
                get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_NTPW), temp)) {
                /* leave as default */
        } else {
@@ -688,7 +722,52 @@ static BOOL init_sam_from_ldap (struct ldapsam_privates *ldap_state,
                ZERO_STRUCT(smbntpwd);
        }
 
-       if (!smbldap_get_single_attribute (ldap_state->smbldap_state->ldap_struct, entry,
+       account_policy_get(AP_PASSWORD_HISTORY, &pwHistLen);
+       if (pwHistLen > 0){
+               uint8 *pwhist = NULL;
+               int i;
+
+               /* We can only store (sizeof(pstring)-1)/64 password history entries. */
+               pwHistLen = MIN(pwHistLen, ((sizeof(temp)-1)/64));
+
+               if ((pwhist = malloc(pwHistLen * PW_HISTORY_ENTRY_LEN)) == NULL){
+                       DEBUG(0, ("init_sam_from_ldap: malloc failed!\n"));
+                       return False;
+               }
+               memset(pwhist, '\0', pwHistLen * PW_HISTORY_ENTRY_LEN);
+
+               if (!smbldap_get_single_pstring (ldap_state->smbldap_state->ldap_struct, entry, 
+                       get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_PWD_HISTORY), temp)) {
+                       /* leave as default - zeros */
+               } else {
+                       BOOL hex_failed = False;
+                       for (i = 0; i < pwHistLen; i++){
+                               /* Get the 16 byte salt. */
+                               if (!pdb_gethexpwd(&temp[i*64], &pwhist[i*PW_HISTORY_ENTRY_LEN])) {
+                                       hex_failed = True;
+                                       break;
+                               }
+                               /* Get the 16 byte MD5 hash of salt+passwd. */
+                               if (!pdb_gethexpwd(&temp[(i*64)+32],
+                                               &pwhist[(i*PW_HISTORY_ENTRY_LEN)+PW_HISTORY_SALT_LEN])) {
+                                       hex_failed = True;
+                                       break;
+                               }
+                       }
+                       if (hex_failed) {
+                               DEBUG(0,("init_sam_from_ldap: Failed to get password history for user %s\n",
+                                       username));
+                               memset(pwhist, '\0', pwHistLen * PW_HISTORY_ENTRY_LEN);
+                       }
+               }
+               if (!pdb_set_pw_history(sampass, pwhist, pwHistLen, PDB_SET)){
+                       SAFE_FREE(pwhist);
+                       return False;
+               }
+               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;
        } else {
@@ -703,11 +782,9 @@ static BOOL init_sam_from_ldap (struct ldapsam_privates *ldap_state,
        pdb_set_hours_len(sampass, hours_len, PDB_SET);
        pdb_set_logon_divs(sampass, logon_divs, PDB_SET);
 
-       pdb_set_munged_dial(sampass, munged_dial, PDB_SET);
+/*     pdb_set_munged_dial(sampass, munged_dial, PDB_SET); */
        
-       /* pdb_set_unknown_3(sampass, unknown3, PDB_SET); */
-
-       if (!smbldap_get_single_attribute(ldap_state->smbldap_state->ldap_struct, entry,
+       if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
                        get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_BAD_PASSWORD_COUNT), temp)) {
                        /* leave as default */
        } else {
@@ -715,7 +792,16 @@ 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_attribute(ldap_state->smbldap_state->ldap_struct, entry,
+       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 */
        } else {
@@ -725,13 +811,58 @@ static BOOL init_sam_from_ldap (struct ldapsam_privates *ldap_state,
 
        /* pdb_set_unknown_6(sampass, unknown6, PDB_SET); */
 
-       pdb_set_hours(sampass, hours, PDB_SET);
-
+       if(!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
+               get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_LOGON_HOURS), temp)) {
+                       /* leave as default */
+       } else {
+               pdb_gethexhours(temp, hours);
+               memset((char *)temp, '\0', strlen(temp) +1);
+               pdb_set_hours(sampass, hours, PDB_SET);
+               ZERO_STRUCT(hours);
+       }
+
+       /* 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 = %u, bad time = %u\n",
+                          (unsigned int)pdb_get_bad_password_count(sampass),
+                          (unsigned int)pdb_get_bad_password_time(sampass)));
+               return True;
+       }
+
+       DEBUG(7, ("ldap time is %u, cache time is %u, bad time = %u\n", 
+                 (unsigned int)ldap_entry_time, (unsigned int)cache_entry->entry_timestamp, 
+                 (unsigned int)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;
 }
 
 /**********************************************************************
- Initialize SAM_ACCOUNT from an LDAP query.
+ Initialize the ldap db from a SAM_ACCOUNT. Called on update.
  (Based on init_buffer_from_sam in pdb_tdb.c)
 *********************************************************************/
 
@@ -851,7 +982,12 @@ 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_USER_WKS), 
                        pdb_get_workstations(sampass));
-
+       
+       if (need_update(sampass, PDB_MUNGEDDIAL))
+               smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+                       get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_MUNGED_DIAL), 
+                       pdb_get_munged_dial(sampass));
+       
        if (need_update(sampass, PDB_SMBHOME))
                smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
                        get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_HOME_PATH), 
@@ -897,39 +1033,148 @@ 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)) {
 
-               pdb_sethexpwd(temp, pdb_get_lanman_passwd(sampass),
-                              pdb_get_acct_ctrl(sampass));
-
-               if (need_update(sampass, PDB_LMPASSWD))
-                       smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
-                               get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_LMPW), 
-                               temp);
-
-               pdb_sethexpwd (temp, pdb_get_nt_passwd(sampass),
-                              pdb_get_acct_ctrl(sampass));
+               if (need_update(sampass, PDB_LMPASSWD)) {
+                       const uchar *lm_pw =  pdb_get_lanman_passwd(sampass);
+                       if (lm_pw) {
+                               pdb_sethexpwd(temp, lm_pw,
+                                             pdb_get_acct_ctrl(sampass));
+                               smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+                                                get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_LMPW), 
+                                                temp);
+                       } else {
+                               smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+                                                get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_LMPW), 
+                                                NULL);
+                       }
+               }
+               if (need_update(sampass, PDB_NTPASSWD)) {
+                       const uchar *nt_pw =  pdb_get_nt_passwd(sampass);
+                       if (nt_pw) {
+                               pdb_sethexpwd(temp, nt_pw,
+                                             pdb_get_acct_ctrl(sampass));
+                               smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+                                                get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_NTPW), 
+                                                temp);
+                       } else {
+                               smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+                                                get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_NTPW), 
+                                                NULL);
+                       }
+               }
 
-               if (need_update(sampass, PDB_NTPASSWD))
+               if (need_update(sampass, PDB_PWHISTORY)) {
+                       int pwHistLen = 0;
+                       account_policy_get(AP_PASSWORD_HISTORY, &pwHistLen);
+                       if (pwHistLen == 0) {
+                               /* Remove any password history from the LDAP store. */
+                               memset(temp, '0', 64); /* NOTE !!!! '0' *NOT '\0' */
+                               temp[64] = '\0';
+                       } else {
+                               int i, currHistLen = 0;
+                               const uint8 *pwhist = pdb_get_pw_history(sampass, &currHistLen);
+                               if (pwhist != NULL) {
+                                       /* We can only store (sizeof(pstring)-1)/64 password history entries. */
+                                       pwHistLen = MIN(pwHistLen, ((sizeof(temp)-1)/64));
+                                       for (i=0; i< pwHistLen && i < currHistLen; i++) {
+                                               /* Store the salt. */
+                                               pdb_sethexpwd(&temp[i*64], &pwhist[i*PW_HISTORY_ENTRY_LEN], 0);
+                                               /* Followed by the md5 hash of salt + md4 hash */
+                                               pdb_sethexpwd(&temp[(i*64)+32],
+                                                       &pwhist[(i*PW_HISTORY_ENTRY_LEN)+PW_HISTORY_SALT_LEN], 0);
+                                               DEBUG(100, ("temp=%s\n", temp));
+                                       }
+                               } 
+                       }
                        smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
-                               get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_NTPW), 
-                               temp);
+                                        get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_PWD_HISTORY), 
+                                        temp);
+               }
 
-               slprintf (temp, sizeof (temp) - 1, "%li", pdb_get_pass_last_set_time(sampass));
-               if (need_update(sampass, PDB_PASSLASTSET))
+               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,
                                get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_PWD_LAST_SET), 
                                temp);
+               }
        }
 
-       /* FIXME: Hours stuff goes in LDAP  */
+       if (need_update(sampass, PDB_HOURS)) {
+               const char *hours = pdb_get_hours(sampass);
+               if (hours) {
+                       pdb_sethexhours(temp, hours);
+                       smbldap_make_mod(ldap_state->smbldap_state->ldap_struct,
+                               existing,
+                               mods,
+                               get_userattr_key2string(ldap_state->schema_ver,
+                                               LDAP_ATTR_LOGON_HOURS),
+                               temp);
+               }
+       }
 
        if (need_update(sampass, PDB_ACCTCTRL))
                smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
                        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=%u, count=%u, time=%u\n",
+                       (unsigned int)pol, (unsigned int)badcount, (unsigned int)badtime));
+
+               if ((badcount >= pol) || (badcount == 0)) {
+                       DEBUG(7, ("making mods to update ldap, count=%u, time=%u\n",
+                               (unsigned int)badcount, (unsigned int)badtime));
+                       slprintf (temp, sizeof (temp) - 1, "%li", (long)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;
+
+                       cache_entry.entry_timestamp = time(NULL);
+                       cache_entry.acct_ctrl = pdb_get_acct_ctrl(sampass);
+                       cache_entry.bad_password_count = badcount;
+                       cache_entry.bad_password_time = badtime;
+
+                       DEBUG(7, ("Updating bad password count and time in login cache\n"));
+                       login_cache_write(sampass, cache_entry);
+               }
+       }
+
        return True;
 }
 
@@ -1009,6 +1254,24 @@ static NTSTATUS ldapsam_getsampwent(struct pdb_methods *my_methods, SAM_ACCOUNT
        return NT_STATUS_OK;
 }
 
+static void append_attr(char ***attr_list, const char *new_attr)
+{
+       int i;
+
+       if (new_attr == NULL) {
+               return;
+       }
+
+       for (i=0; (*attr_list)[i] != NULL; i++) {
+               ;
+       }
+
+       (*attr_list) = Realloc((*attr_list), sizeof(**attr_list) * (i+2));
+       SMB_ASSERT((*attr_list) != NULL);
+       (*attr_list)[i] = strdup(new_attr);
+       (*attr_list)[i+1] = NULL;
+}
+
 /**********************************************************************
 Get SAM_ACCOUNT entry from LDAP by username.
 *********************************************************************/
@@ -1017,13 +1280,14 @@ static NTSTATUS ldapsam_getsampwnam(struct pdb_methods *my_methods, SAM_ACCOUNT
 {
        NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
        struct ldapsam_privates *ldap_state = (struct ldapsam_privates *)my_methods->private_data;
-       LDAPMessage *result;
-       LDAPMessage *entry;
+       LDAPMessage *result = NULL;
+       LDAPMessage *entry = NULL;
        int count;
        char ** attr_list;
        int rc;
        
        attr_list = get_userattr_list( ldap_state->schema_ver );
+       append_attr(&attr_list, get_userattr_key2string(ldap_state->schema_ver,LDAP_ATTR_MOD_TIMESTAMP));
        rc = ldapsam_search_suffix_by_name(ldap_state, sname, &result, attr_list);
        free_attr_list( attr_list );
 
@@ -1069,6 +1333,7 @@ static int ldapsam_get_ldap_user_by_sid(struct ldapsam_privates *ldap_state,
        switch ( ldap_state->schema_ver ) {
                case SCHEMAVER_SAMBASAMACCOUNT:
                        attr_list = get_userattr_list(ldap_state->schema_ver);
+                       append_attr(&attr_list, get_userattr_key2string(ldap_state->schema_ver,LDAP_ATTR_MOD_TIMESTAMP));
                        rc = ldapsam_search_suffix_by_sid(ldap_state, sid, result, attr_list);
                        free_attr_list( attr_list );
 
@@ -1099,8 +1364,8 @@ static int ldapsam_get_ldap_user_by_sid(struct ldapsam_privates *ldap_state,
 static NTSTATUS ldapsam_getsampwsid(struct pdb_methods *my_methods, SAM_ACCOUNT * user, const DOM_SID *sid)
 {
        struct ldapsam_privates *ldap_state = (struct ldapsam_privates *)my_methods->private_data;
-       LDAPMessage *result;
-       LDAPMessage *entry;
+       LDAPMessage *result = NULL;
+       LDAPMessage *entry = NULL;
        int count;
        int rc;
        fstring sid_string;
@@ -1131,7 +1396,7 @@ static NTSTATUS ldapsam_getsampwsid(struct pdb_methods *my_methods, SAM_ACCOUNT
        }
 
        if (!init_sam_from_ldap(ldap_state, user, entry)) {
-               DEBUG(1,("ldapsam_getsampwrid: init_sam_from_ldap failed!\n"));
+               DEBUG(1,("ldapsam_getsampwsid: init_sam_from_ldap failed!\n"));
                ldap_msgfree(result);
                return NT_STATUS_NO_SUCH_USER;
        }
@@ -1142,6 +1407,66 @@ static NTSTATUS ldapsam_getsampwsid(struct pdb_methods *my_methods, SAM_ACCOUNT
        return NT_STATUS_OK;
 }      
 
+static BOOL ldapsam_can_pwchange_exop(struct smbldap_state *ldap_state)
+{
+       LDAPMessage *msg = NULL;
+       LDAPMessage *entry = NULL;
+       char **values = NULL;
+       char *attrs[] = { "supportedExtension", NULL };
+       int rc, num_result, num_values, i;
+       BOOL result = False;
+
+       rc = smbldap_search(ldap_state, "", LDAP_SCOPE_BASE, "(objectclass=*)",
+                           attrs, 0, &msg);
+
+       if (rc != LDAP_SUCCESS) {
+               DEBUG(3, ("Could not search rootDSE\n"));
+               return False;
+       }
+
+       num_result = ldap_count_entries(ldap_state->ldap_struct, msg);
+
+       if (num_result != 1) {
+               DEBUG(3, ("Expected one rootDSE, got %d\n", num_result));
+               goto done;
+       }
+
+       entry = ldap_first_entry(ldap_state->ldap_struct, msg);
+
+       if (entry == NULL) {
+               DEBUG(3, ("Could not retrieve rootDSE\n"));
+               goto done;
+       }
+
+       values = ldap_get_values(ldap_state->ldap_struct, entry,
+                                "supportedExtension");
+
+       if (values == NULL) {
+               DEBUG(9, ("LDAP Server does not support any extensions\n"));
+               goto done;
+       }
+
+       num_values = ldap_count_values(values);
+
+       if (num_values == 0) {
+               DEBUG(9, ("LDAP Server does not support any extensions\n"));
+               goto done;
+       }
+
+       for (i=0; i<num_values; i++) {
+               if (strcmp(values[i], LDAP_EXOP_MODIFY_PASSWD) == 0)
+                       result = True;
+       }
+
+ done:
+       if (values != NULL)
+               ldap_value_free(values);
+       if (msg != NULL)
+               ldap_msgfree(msg);
+
+       return result;
+}
+
 /********************************************************************
  Do the actual modification - also change a plaintext passord if 
  it it set.
@@ -1205,6 +1530,12 @@ static NTSTATUS ldapsam_modify_entry(struct pdb_methods *my_methods,
                char *utf8_password;
                char *utf8_dn;
 
+               if (!ldapsam_can_pwchange_exop(ldap_state->smbldap_state)) {
+                       DEBUG(2, ("ldap password change requested, but LDAP "
+                                 "server does not support it -- ignoring\n"));
+                       return NT_STATUS_OK;
+               }
+
                if (push_utf8_allocate(&utf8_password, pdb_get_plaintext_passwd(newpwd)) == (size_t)-1) {
                        return NT_STATUS_NO_MEMORY;
                }
@@ -1240,8 +1571,23 @@ static NTSTATUS ldapsam_modify_entry(struct pdb_methods *my_methods,
                                                     LDAP_EXOP_MODIFY_PASSWD,
                                                     bv, NULL, NULL, &retoid, 
                                                     &retdata)) != LDAP_SUCCESS) {
-                       DEBUG(0,("ldapsam_modify_entry: LDAP Password could not be changed for user %s: %s\n",
-                               pdb_get_username(newpwd),ldap_err2string(rc)));
+                       char *ld_error = NULL;
+
+                       if (rc == LDAP_OBJECT_CLASS_VIOLATION) {
+                               DEBUG(3, ("Could not set userPassword "
+                                         "attribute due to an objectClass "
+                                         "violation -- ignoring\n"));
+                               ber_bvfree(bv);
+                               return NT_STATUS_OK;
+                       }
+
+                       ldap_get_option(ldap_state->smbldap_state->ldap_struct, LDAP_OPT_ERROR_STRING,
+                                       &ld_error);
+                       DEBUG(0,("ldapsam_modify_entry: LDAP Password could not be changed for user %s: %s\n\t%s\n",
+                               pdb_get_username(newpwd), ldap_err2string(rc), ld_error?ld_error:"unknown"));
+                       SAFE_FREE(ld_error);
+                       ber_bvfree(bv);
+                       return NT_STATUS_UNSUCCESSFUL;
                } else {
                        DEBUG(3,("ldapsam_modify_entry: LDAP Password changed for user %s\n",pdb_get_username(newpwd)));
 #ifdef DEBUG_PASSWORD
@@ -1264,7 +1610,7 @@ static NTSTATUS ldapsam_delete_sam_account(struct pdb_methods *my_methods, SAM_A
        struct ldapsam_privates *ldap_state = (struct ldapsam_privates *)my_methods->private_data;
        const char *sname;
        int rc;
-       LDAPMessage *result;
+       LDAPMessage *result = NULL;
        NTSTATUS ret;
        char **attr_list;
        fstring objclass;
@@ -1278,7 +1624,7 @@ static NTSTATUS ldapsam_delete_sam_account(struct pdb_methods *my_methods, SAM_A
 
        DEBUG (3, ("ldapsam_delete_sam_account: Deleting user %s from LDAP.\n", sname));
 
-       attr_list= get_userattr_list( ldap_state->schema_ver );
+       attr_list= get_userattr_delete_list( ldap_state->schema_ver );
        rc = ldapsam_search_suffix_by_name(ldap_state, sname, &result, attr_list);
 
        if (rc != LDAP_SUCCESS)  {
@@ -1328,9 +1674,9 @@ static NTSTATUS ldapsam_update_sam_account(struct pdb_methods *my_methods, SAM_A
        struct ldapsam_privates *ldap_state = (struct ldapsam_privates *)my_methods->private_data;
        int rc = 0;
        char *dn;
-       LDAPMessage *result;
-       LDAPMessage *entry;
-       LDAPMod **mods;
+       LDAPMessage *result = NULL;
+       LDAPMessage *entry = NULL;
+       LDAPMod **mods = NULL;
        char **attr_list;
 
        result = pdb_get_backend_private_data(newpwd, my_methods);
@@ -1361,13 +1707,14 @@ static NTSTATUS ldapsam_update_sam_account(struct pdb_methods *my_methods, SAM_A
                                element_is_changed)) {
                DEBUG(0, ("ldapsam_update_sam_account: init_ldap_from_sam failed!\n"));
                SAFE_FREE(dn);
+               if (mods != NULL)
+                       ldap_mods_free(mods,True);
                return NT_STATUS_UNSUCCESSFUL;
        }
        
        if (mods == NULL) {
                DEBUG(4,("ldapsam_update_sam_account: mods is empty: nothing to update for user: %s\n",
                         pdb_get_username(newpwd)));
-               ldap_mods_free(mods, True);
                SAFE_FREE(dn);
                return NT_STATUS_OK;
        }
@@ -1458,6 +1805,7 @@ static NTSTATUS ldapsam_add_sam_account(struct pdb_methods *my_methods, SAM_ACCO
                                DEBUG(0,("ldapsam_add_sam_account: SID '%s' already in the base, with samba attributes\n", 
                                         sid_to_string(sid_string, sid)));
                                free_attr_list( attr_list );
+                               ldap_msgfree(result);
                                return NT_STATUS_UNSUCCESSFUL;
                        }
                        ldap_msgfree(result);
@@ -1514,6 +1862,11 @@ static NTSTATUS ldapsam_add_sam_account(struct pdb_methods *my_methods, SAM_ACCO
                         LDAP_OBJ_IDMAP_ENTRY,
                         LDAP_OBJ_SID_ENTRY);
                
+               /* free old result before doing a new search */
+               if (result != NULL) {
+                       ldap_msgfree(result);
+                       result = NULL;
+               }
                rc = smbldap_search_suffix(ldap_state->smbldap_state, 
                                           filter, attr_list, &result);
                        
@@ -1566,6 +1919,8 @@ static NTSTATUS ldapsam_add_sam_account(struct pdb_methods *my_methods, SAM_ACCO
                                element_is_set_or_changed)) {
                DEBUG(0, ("ldapsam_add_sam_account: init_ldap_from_sam failed!\n"));
                ldap_msgfree(result);
+               if (mods != NULL)
+                       ldap_mods_free(mods,True);
                return NT_STATUS_UNSUCCESSFUL;          
        }
        
@@ -1612,9 +1967,6 @@ static int ldapsam_search_one_group (struct ldapsam_privates *ldap_state,
        int rc;
        char **attr_list;
 
-       DEBUG(2, ("ldapsam_search_one_group: searching for:[%s]\n", filter));
-
-
        attr_list = get_attr_list(groupmap_attr_list);
        rc = smbldap_search(ldap_state->smbldap_state, 
                            lp_ldap_group_suffix (), scope,
@@ -1626,9 +1978,9 @@ static int ldapsam_search_one_group (struct ldapsam_privates *ldap_state,
                ldap_get_option(ldap_state->smbldap_state->ldap_struct, LDAP_OPT_ERROR_STRING,
                                &ld_error);
                DEBUG(0, ("ldapsam_search_one_group: "
-                         "Problem during the LDAP search: LDAP error: %s (%s)",
+                         "Problem during the LDAP search: LDAP error: %s (%s)\n",
                          ld_error?ld_error:"(unknown)", ldap_err2string(rc)));
-               DEBUG(3, ("ldapsam_search_one_group: Query was: %s, %s\n",
+               DEBUGADD(3, ("ldapsam_search_one_group: Query was: %s, %s\n",
                          lp_ldap_group_suffix(), filter));
                SAFE_FREE(ld_error);
        }
@@ -1650,7 +2002,7 @@ static BOOL init_group_from_ldap(struct ldapsam_privates *ldap_state,
                return False;
        }
 
-       if (!smbldap_get_single_attribute(ldap_state->smbldap_state->ldap_struct, entry, 
+       if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry, 
                        get_attr_key2string(groupmap_attr_list, LDAP_ATTR_GIDNUMBER), temp)) {
                DEBUG(0, ("init_group_from_ldap: Mandatory attribute %s not found\n", 
                        get_attr_key2string( groupmap_attr_list, LDAP_ATTR_GIDNUMBER)));
@@ -1660,15 +2012,19 @@ static BOOL init_group_from_ldap(struct ldapsam_privates *ldap_state,
 
        map->gid = (gid_t)atol(temp);
 
-       if (!smbldap_get_single_attribute(ldap_state->smbldap_state->ldap_struct, entry, 
+       if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry, 
                        get_attr_key2string( groupmap_attr_list, LDAP_ATTR_GROUP_SID), temp)) {
                DEBUG(0, ("init_group_from_ldap: Mandatory attribute %s not found\n",
                        get_attr_key2string( groupmap_attr_list, LDAP_ATTR_GROUP_SID)));
                return False;
        }
-       string_to_sid(&map->sid, temp);
+       
+       if (!string_to_sid(&map->sid, temp)) {
+               DEBUG(1, ("SID string [%s] could not be read as a valid SID\n", temp));
+               return False;
+       }
 
-       if (!smbldap_get_single_attribute(ldap_state->smbldap_state->ldap_struct, entry, 
+       if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry, 
                        get_attr_key2string( groupmap_attr_list, LDAP_ATTR_GROUP_TYPE), temp)) {
                DEBUG(0, ("init_group_from_ldap: Mandatory attribute %s not found\n",
                        get_attr_key2string( groupmap_attr_list, LDAP_ATTR_GROUP_TYPE)));
@@ -1682,10 +2038,10 @@ static BOOL init_group_from_ldap(struct ldapsam_privates *ldap_state,
                return False;
        }
 
-       if (!smbldap_get_single_attribute(ldap_state->smbldap_state->ldap_struct, entry, 
+       if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry, 
                        get_attr_key2string( groupmap_attr_list, LDAP_ATTR_DISPLAY_NAME), temp)) {
                temp[0] = '\0';
-               if (!smbldap_get_single_attribute(ldap_state->smbldap_state->ldap_struct, entry, 
+               if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry, 
                        get_attr_key2string( groupmap_attr_list, LDAP_ATTR_CN), temp)) 
                {
                        DEBUG(0, ("init_group_from_ldap: Attributes cn not found either \
@@ -1695,7 +2051,7 @@ for gidNumber(%lu)\n",(unsigned long)map->gid));
        }
        fstrcpy(map->nt_name, temp);
 
-       if (!smbldap_get_single_attribute(ldap_state->smbldap_state->ldap_struct, entry, 
+       if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry, 
                        get_attr_key2string( groupmap_attr_list, LDAP_ATTR_DESC), temp)) {
                temp[0] = '\0';
        }
@@ -1722,6 +2078,7 @@ static BOOL init_ldap_from_group(LDAP *ldap_struct,
        *mods = NULL;
 
        sid_to_string(tmp, &map->sid);
+
        smbldap_make_mod(ldap_struct, existing, mods, 
                get_attr_key2string(groupmap_attr_list, LDAP_ATTR_GROUP_SID), tmp);
        pstr_sprintf(tmp, "%i", map->sid_name_use);
@@ -1745,8 +2102,8 @@ static NTSTATUS ldapsam_getgroup(struct pdb_methods *methods,
 {
        struct ldapsam_privates *ldap_state =
                (struct ldapsam_privates *)methods->private_data;
-       LDAPMessage *result;
-       LDAPMessage *entry;
+       LDAPMessage *result = NULL;
+       LDAPMessage *entry = NULL;
        int count;
 
        if (ldapsam_search_one_group(ldap_state, filter, &result)
@@ -1851,8 +2208,8 @@ static int ldapsam_search_one_group_by_gid(struct ldapsam_privates *ldap_state,
 {
        pstring filter;
 
-       pstr_sprintf(filter, "(&(objectClass=%s)(%s=%lu))", 
-               LDAP_OBJ_POSIXGROUP,
+       pstr_sprintf(filter, "(&(|(objectClass=%s)(objectclass=%s))(%s=%lu))", 
+               LDAP_OBJ_POSIXGROUP, LDAP_OBJ_IDMAP_ENTRY,
                get_attr_key2string(groupmap_attr_list, LDAP_ATTR_GIDNUMBER),
                (unsigned long)gid);
 
@@ -1893,6 +2250,37 @@ static NTSTATUS ldapsam_add_group_mapping_entry(struct pdb_methods *methods,
 
        count = ldap_count_entries(ldap_state->smbldap_state->ldap_struct, result);
 
+       if ( count == 0 ) {
+               /* There's no posixGroup account, let's try to find an
+                * appropriate idmap entry for aliases */
+
+               pstring suffix;
+               pstring filter;
+               char **attr_list;
+
+               ldap_msgfree(result);
+
+               pstrcpy( suffix, lp_ldap_idmap_suffix() );
+               pstr_sprintf(filter, "(&(objectClass=%s)(%s=%u))",
+                            LDAP_OBJ_IDMAP_ENTRY, LDAP_ATTRIBUTE_GIDNUMBER,
+                            map->gid);
+               
+               attr_list = get_attr_list( sidmap_attr_list );
+               rc = smbldap_search(ldap_state->smbldap_state, suffix,
+                                   LDAP_SCOPE_SUBTREE, filter, attr_list,
+                                   0, &result);
+
+               free_attr_list(attr_list);
+
+               if (rc != LDAP_SUCCESS) {
+                       DEBUG(3,("Failure looking up entry (%s)\n",
+                                ldap_err2string(rc) ));
+                       ldap_msgfree(result);
+                       return NT_STATUS_UNSUCCESSFUL;
+               }
+       }
+                          
+       count = ldap_count_entries(ldap_state->smbldap_state->ldap_struct, result);
        if ( count == 0 ) {
                ldap_msgfree(result);
                return NT_STATUS_UNSUCCESSFUL;
@@ -1957,10 +2345,10 @@ static NTSTATUS ldapsam_update_group_mapping_entry(struct pdb_methods *methods,
        struct ldapsam_privates *ldap_state =
                (struct ldapsam_privates *)methods->private_data;
        int rc;
-       char *dn;
-       LDAPMessage *result;
-       LDAPMessage *entry;
-       LDAPMod **mods;
+       char *dn = NULL;
+       LDAPMessage *result = NULL;
+       LDAPMessage *entry = NULL;
+       LDAPMod **mods = NULL;
 
        rc = ldapsam_search_one_group_by_gid(ldap_state, map->gid, &result);
 
@@ -1980,24 +2368,27 @@ static NTSTATUS ldapsam_update_group_mapping_entry(struct pdb_methods *methods,
                                  result, &mods, map)) {
                DEBUG(0, ("ldapsam_update_group_mapping_entry: init_ldap_from_group failed\n"));
                ldap_msgfree(result);
+               if (mods != NULL)
+                       ldap_mods_free(mods,True);
                return NT_STATUS_UNSUCCESSFUL;
        }
 
-       ldap_msgfree(result);
-
        if (mods == NULL) {
                DEBUG(4, ("ldapsam_update_group_mapping_entry: mods is empty: nothing to do\n"));
-               return NT_STATUS_UNSUCCESSFUL;
+               ldap_msgfree(result);
+               return NT_STATUS_OK;
        }
 
        dn = smbldap_get_dn(ldap_state->smbldap_state->ldap_struct, entry);
        if (!dn) {
+               ldap_msgfree(result);
                return NT_STATUS_UNSUCCESSFUL;
        }
        rc = smbldap_modify(ldap_state->smbldap_state, dn, mods);
        SAFE_FREE(dn);
 
        ldap_mods_free(mods, True);
+       ldap_msgfree(result);
 
        if (rc != LDAP_SUCCESS) {
                char *ld_error = NULL;
@@ -2006,6 +2397,7 @@ static NTSTATUS ldapsam_update_group_mapping_entry(struct pdb_methods *methods,
                DEBUG(0, ("ldapsam_update_group_mapping_entry: failed to modify group %lu error: %s (%s)\n", (unsigned long)map->gid, 
                          ld_error ? ld_error : "(unknown)", ldap_err2string(rc)));
                SAFE_FREE(ld_error);
+               return NT_STATUS_UNSUCCESSFUL;
        }
 
        DEBUG(2, ("ldapsam_update_group_mapping_entry: successfully modified group %lu in LDAP\n", (unsigned long)map->gid));
@@ -2020,7 +2412,7 @@ static NTSTATUS ldapsam_delete_group_mapping_entry(struct pdb_methods *methods,
 {
        struct ldapsam_privates *ldap_state = (struct ldapsam_privates *)methods->private_data;
        pstring sidstring, filter;
-       LDAPMessage *result;
+       LDAPMessage *result = NULL;
        int rc;
        NTSTATUS ret;
        char **attr_list;
@@ -2070,7 +2462,7 @@ static NTSTATUS ldapsam_setsamgrent(struct pdb_methods *my_methods, BOOL update)
                return NT_STATUS_UNSUCCESSFUL;
        }
 
-       DEBUG(2, ("ldapsam_setsampwent: %d entries in the base!\n",
+       DEBUG(2, ("ldapsam_setsamgrent: %d entries in the base!\n",
                  ldap_count_entries(ldap_state->smbldap_state->ldap_struct,
                                     ldap_state->result)));
 
@@ -2164,6 +2556,256 @@ static NTSTATUS ldapsam_enum_group_mapping(struct pdb_methods *methods,
        return NT_STATUS_OK;
 }
 
+static NTSTATUS ldapsam_modify_aliasmem(struct pdb_methods *methods,
+                                       const DOM_SID *alias,
+                                       const DOM_SID *member,
+                                       int modop)
+{
+       struct ldapsam_privates *ldap_state =
+               (struct ldapsam_privates *)methods->private_data;
+       char *dn;
+       LDAPMessage *result = NULL;
+       LDAPMessage *entry = NULL;
+       int count;
+       LDAPMod **mods = NULL;
+       int rc;
+
+       pstring filter;
+
+       pstr_sprintf(filter, "(&(|(objectClass=%s)(objectclass=%s))(%s=%s))",
+                    LDAP_OBJ_GROUPMAP, LDAP_OBJ_IDMAP_ENTRY,
+                    get_attr_key2string(groupmap_attr_list,
+                                        LDAP_ATTR_GROUP_SID),
+                    sid_string_static(alias));
+
+       if (ldapsam_search_one_group(ldap_state, filter,
+                                    &result) != LDAP_SUCCESS)
+               return NT_STATUS_NO_SUCH_ALIAS;
+
+       count = ldap_count_entries(ldap_state->smbldap_state->ldap_struct,
+                                  result);
+
+       if (count < 1) {
+               DEBUG(4, ("ldapsam_modify_aliasmem: Did not find alias\n"));
+               ldap_msgfree(result);
+               return NT_STATUS_NO_SUCH_ALIAS;
+       }
+
+       if (count > 1) {
+               DEBUG(1, ("ldapsam_modify_aliasmem: Duplicate entries for filter %s: "
+                         "count=%d\n", filter, count));
+               ldap_msgfree(result);
+               return NT_STATUS_NO_SUCH_ALIAS;
+       }
+
+       entry = ldap_first_entry(ldap_state->smbldap_state->ldap_struct,
+                                result);
+
+       if (!entry) {
+               ldap_msgfree(result);
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       dn = smbldap_get_dn(ldap_state->smbldap_state->ldap_struct, entry);
+       if (!dn) {
+               ldap_msgfree(result);
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       smbldap_set_mod(&mods, modop,
+                       get_attr_key2string(groupmap_attr_list,
+                                           LDAP_ATTR_SID_LIST),
+                       sid_string_static(member));
+
+       rc = smbldap_modify(ldap_state->smbldap_state, dn, mods);
+
+       ldap_mods_free(mods, True);
+       ldap_msgfree(result);
+
+       if (rc != LDAP_SUCCESS) {
+               char *ld_error = NULL;
+               ldap_get_option(ldap_state->smbldap_state->ldap_struct,
+                               LDAP_OPT_ERROR_STRING,&ld_error);
+               
+               DEBUG(0, ("ldapsam_modify_aliasmem: Could not modify alias "
+                         "for %s, error: %s (%s)\n", dn, ldap_err2string(rc),
+                         ld_error?ld_error:"unknown"));
+               SAFE_FREE(ld_error);
+               SAFE_FREE(dn);
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       SAFE_FREE(dn);
+
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS ldapsam_add_aliasmem(struct pdb_methods *methods,
+                                    const DOM_SID *alias,
+                                    const DOM_SID *member)
+{
+       return ldapsam_modify_aliasmem(methods, alias, member, LDAP_MOD_ADD);
+}
+
+static NTSTATUS ldapsam_del_aliasmem(struct pdb_methods *methods,
+                                    const DOM_SID *alias,
+                                    const DOM_SID *member)
+{
+       return ldapsam_modify_aliasmem(methods, alias, member,
+                                      LDAP_MOD_DELETE);
+}
+
+static NTSTATUS ldapsam_enum_aliasmem(struct pdb_methods *methods,
+                                     const DOM_SID *alias, DOM_SID **members,
+                                     int *num_members)
+{
+       struct ldapsam_privates *ldap_state =
+               (struct ldapsam_privates *)methods->private_data;
+       LDAPMessage *result = NULL;
+       LDAPMessage *entry = NULL;
+       int count;
+       char **values;
+       int i;
+       pstring filter;
+
+       *members = NULL;
+       *num_members = 0;
+
+       pstr_sprintf(filter, "(&(|(objectClass=%s)(objectclass=%s))(%s=%s))",
+                    LDAP_OBJ_GROUPMAP, LDAP_OBJ_IDMAP_ENTRY,
+                    get_attr_key2string(groupmap_attr_list,
+                                        LDAP_ATTR_GROUP_SID),
+                    sid_string_static(alias));
+
+       if (ldapsam_search_one_group(ldap_state, filter,
+                                    &result) != LDAP_SUCCESS)
+               return NT_STATUS_NO_SUCH_ALIAS;
+
+       count = ldap_count_entries(ldap_state->smbldap_state->ldap_struct,
+                                  result);
+
+       if (count < 1) {
+               DEBUG(4, ("ldapsam_enum_aliasmem: Did not find alias\n"));
+               ldap_msgfree(result);
+               return NT_STATUS_NO_SUCH_ALIAS;
+       }
+
+       if (count > 1) {
+               DEBUG(1, ("ldapsam_enum_aliasmem: Duplicate entries for filter %s: "
+                         "count=%d\n", filter, count));
+               ldap_msgfree(result);
+               return NT_STATUS_NO_SUCH_ALIAS;
+       }
+
+       entry = ldap_first_entry(ldap_state->smbldap_state->ldap_struct,
+                                result);
+
+       if (!entry) {
+               ldap_msgfree(result);
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       values = ldap_get_values(ldap_state->smbldap_state->ldap_struct,
+                                entry,
+                                get_attr_key2string(groupmap_attr_list,
+                                                    LDAP_ATTR_SID_LIST));
+
+       if (values == NULL) {
+               ldap_msgfree(result);
+               return NT_STATUS_OK;
+       }
+
+       count = ldap_count_values(values);
+
+       for (i=0; i<count; i++) {
+               DOM_SID member;
+
+               if (!string_to_sid(&member, values[i]))
+                       continue;
+
+               add_sid_to_array(&member, members, num_members);
+       }
+
+       ldap_value_free(values);
+       ldap_msgfree(result);
+
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS ldapsam_alias_memberships(struct pdb_methods *methods,
+                                         const DOM_SID *members,
+                                         int num_members,
+                                         DOM_SID **aliases, int *num_aliases)
+{
+       struct ldapsam_privates *ldap_state =
+               (struct ldapsam_privates *)methods->private_data;
+       LDAP *ldap_struct;
+
+       char *attrs[] = { LDAP_ATTRIBUTE_SID, NULL };
+
+       LDAPMessage *result = NULL;
+       LDAPMessage *entry = NULL;
+       int i;
+       int rc;
+       char *filter;
+       TALLOC_CTX *mem_ctx;
+
+       mem_ctx = talloc_init("ldapsam_alias_memberships");
+
+       if (mem_ctx == NULL)
+               return NT_STATUS_NO_MEMORY;
+
+       /* This query could be further optimized by adding a
+          (&(sambaSID=<domain-sid>*)) so that only those aliases that are
+          asked for in the getuseraliases are returned. */        
+
+       filter = talloc_asprintf(mem_ctx,
+                                "(&(|(objectclass=%s)(objectclass=%s))(|",
+                                LDAP_OBJ_GROUPMAP, LDAP_OBJ_IDMAP_ENTRY);
+
+       for (i=0; i<num_members; i++)
+               filter = talloc_asprintf(mem_ctx, "%s(sambaSIDList=%s)",
+                                        filter,
+                                        sid_string_static(&members[i]));
+
+       filter = talloc_asprintf(mem_ctx, "%s))", filter);
+
+       rc = smbldap_search(ldap_state->smbldap_state, lp_ldap_group_suffix(),
+                           LDAP_SCOPE_SUBTREE, filter, attrs, 0, &result);
+
+       talloc_destroy(mem_ctx);
+
+       if (rc != LDAP_SUCCESS)
+               return NT_STATUS_UNSUCCESSFUL;
+
+       *aliases = NULL;
+       *num_aliases = 0;
+
+       ldap_struct = ldap_state->smbldap_state->ldap_struct;
+
+       for (entry = ldap_first_entry(ldap_struct, result);
+            entry != NULL;
+            entry = ldap_next_entry(ldap_struct, entry))
+       {
+               fstring sid_str;
+               DOM_SID sid;
+
+               if (!smbldap_get_single_attribute(ldap_struct, entry,
+                                                 LDAP_ATTRIBUTE_SID,
+                                                 sid_str,
+                                                 sizeof(sid_str)-1))
+                       continue;
+
+               if (!string_to_sid(&sid, sid_str))
+                       continue;
+
+               add_sid_to_array_unique(&sid, aliases, num_aliases);
+       }
+
+       ldap_msgfree(result);
+       return NT_STATUS_OK;
+}
+
 /**********************************************************************
  Housekeeping
  *********************************************************************/
@@ -2174,6 +2816,11 @@ static void free_private_data(void **vp)
 
        smbldap_free_struct(&(*ldap_state)->smbldap_state);
 
+       if ((*ldap_state)->result != NULL) {
+               ldap_msgfree((*ldap_state)->result);
+               (*ldap_state)->result = NULL;
+       }
+
        *ldap_state = NULL;
 
        /* No need to free any further, as it is talloc()ed */
@@ -2297,13 +2944,18 @@ static NTSTATUS pdb_init_ldapsam(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_met
 
        (*pdb_method)->name = "ldapsam";
 
+       (*pdb_method)->add_aliasmem = ldapsam_add_aliasmem;
+       (*pdb_method)->del_aliasmem = ldapsam_del_aliasmem;
+       (*pdb_method)->enum_aliasmem = ldapsam_enum_aliasmem;
+       (*pdb_method)->enum_alias_memberships = ldapsam_alias_memberships;
+
        ldap_state = (*pdb_method)->private_data;
        ldap_state->schema_ver = SCHEMAVER_SAMBASAMACCOUNT;
 
        /* Try to setup the Domain Name, Domain SID, algorithmic rid base */
        
        nt_status = smbldap_search_domain_info(ldap_state->smbldap_state, &result, 
-               ldap_state->domain_name, True);
+                                              ldap_state->domain_name, True);
        
        if ( !NT_STATUS_IS_OK(nt_status) ) {
                DEBUG(2, ("pdb_init_ldapsam: WARNING: Could not get domain info, nor add one to the domain\n"));
@@ -2322,26 +2974,36 @@ and will risk BDCs having inconsistant SIDs\n"));
                return NT_STATUS_UNSUCCESSFUL;
        }
 
-       if (smbldap_get_single_attribute(ldap_state->smbldap_state->ldap_struct, entry, 
+       if (smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry, 
                                 get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_USER_SID), 
                                 domain_sid_string)) {
                BOOL found_sid;
-               string_to_sid(&ldap_domain_sid, domain_sid_string);
+               if (!string_to_sid(&ldap_domain_sid, domain_sid_string)) {
+                       DEBUG(1, ("pdb_init_ldapsam: SID [%s] could not be read as a valid SID\n", domain_sid_string));
+                       return NT_STATUS_INVALID_PARAMETER;
+               }
                found_sid = secrets_fetch_domain_sid(ldap_state->domain_name, &secrets_domain_sid);
                if (!found_sid || !sid_equal(&secrets_domain_sid, &ldap_domain_sid)) {
+                       fstring new_sid_str, old_sid_str;
+                       DEBUG(1, ("pdb_init_ldapsam: Resetting SID for domain %s based on pdb_ldap results %s -> %s\n",
+                                 ldap_state->domain_name, 
+                                 sid_to_string(old_sid_str, &secrets_domain_sid),
+                                 sid_to_string(new_sid_str, &ldap_domain_sid)));
+                       
                        /* reset secrets.tdb sid */
                        secrets_store_domain_sid(ldap_state->domain_name, &ldap_domain_sid);
+                       DEBUG(1, ("New global sam SID: %s\n", sid_to_string(new_sid_str, get_global_sam_sid())));
                }
                sid_copy(&ldap_state->domain_sid, &ldap_domain_sid);
        }
 
-       if (smbldap_get_single_attribute(ldap_state->smbldap_state->ldap_struct, entry, 
-                                get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_ALGORITHMIC_RID_BASE), 
+       if (smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry, 
+                                get_attr_key2string( dominfo_attr_list, LDAP_ATTR_ALGORITHMIC_RID_BASE ),
                                 alg_rid_base_string)) {
                alg_rid_base = (uint32)atol(alg_rid_base_string);
                if (alg_rid_base != algorithmic_rid_base()) {
-                       DEBUG(0, ("pdb_init_ldapsam: The value of 'algorithmic RID base' has changed since the LDAP\n\
-database was initialised.  Aborting. \n"));
+                       DEBUG(0, ("The value of 'algorithmic RID base' has changed since the LDAP\n"
+                                 "database was initialised.  Aborting. \n"));
                        ldap_msgfree(result);
                        return NT_STATUS_UNSUCCESSFUL;
                }