diff options
-rw-r--r-- | source3/auth/auth_sam.c | 2 | ||||
-rw-r--r-- | source3/passdb/passdb.c | 2 | ||||
-rw-r--r-- | source3/passdb/pdb_get_set.c | 68 | ||||
-rw-r--r-- | source3/passdb/pdb_interface.c | 41 | ||||
-rw-r--r-- | source3/passdb/pdb_ldap.c | 2 | ||||
-rw-r--r-- | source3/rpc_server/srv_netlog_nt.c | 2 | ||||
-rw-r--r-- | source3/rpc_server/srv_samr_nt.c | 118 | ||||
-rw-r--r-- | source3/smbd/chgpasswd.c | 45 |
8 files changed, 163 insertions, 117 deletions
diff --git a/source3/auth/auth_sam.c b/source3/auth/auth_sam.c index ec405dd2be..847315ef88 100644 --- a/source3/auth/auth_sam.c +++ b/source3/auth/auth_sam.c @@ -168,7 +168,7 @@ static NTSTATUS sam_account_ok(TALLOC_CTX *mem_ctx, time_t last_set_time = pdb_get_pass_last_set_time(sampass); /* check for immediate expiry "must change at next logon" */ - if (must_change_time == 0 && last_set_time != 0) { + if (last_set_time == 0) { DEBUG(1,("sam_account_ok: Account for user '%s' password must change!.\n", pdb_get_username(sampass))); return NT_STATUS_PASSWORD_MUST_CHANGE; } diff --git a/source3/passdb/passdb.c b/source3/passdb/passdb.c index da3ddb3914..4bdceec571 100644 --- a/source3/passdb/passdb.c +++ b/source3/passdb/passdb.c @@ -1106,7 +1106,7 @@ uint32 init_buffer_from_sam_v3 (uint8 **buf, struct samu *sampass, BOOL size_onl logoff_time = (uint32)pdb_get_logoff_time(sampass); kickoff_time = (uint32)pdb_get_kickoff_time(sampass); bad_password_time = (uint32)pdb_get_bad_password_time(sampass); - pass_can_change_time = (uint32)pdb_get_pass_can_change_time(sampass); + pass_can_change_time = (uint32)pdb_get_pass_can_change_time_noncalc(sampass); pass_must_change_time = (uint32)pdb_get_pass_must_change_time(sampass); pass_last_set_time = (uint32)pdb_get_pass_last_set_time(sampass); diff --git a/source3/passdb/pdb_get_set.c b/source3/passdb/pdb_get_set.c index 7aac8f5856..62898f3dac 100644 --- a/source3/passdb/pdb_get_set.c +++ b/source3/passdb/pdb_get_set.c @@ -74,15 +74,34 @@ time_t pdb_get_pass_can_change_time(const struct samu *sampass) { uint32 allow; + /* if the last set time is zero, it means the user cannot + change their password, and this time must be zero. jmcd + */ if (sampass->pass_last_set_time == 0) return (time_t) 0; + /* if the time is max, and the field has been changed, + we're trying to update this real value from the sampass + to indicate that the user cannot change their password. jmcd + */ + if (sampass->pass_can_change_time == get_time_t_max() && + pdb_get_init_flags(sampass, PDB_CANCHANGETIME) == PDB_CHANGED) + return sampass->pass_can_change_time; + if (!pdb_get_account_policy(AP_MIN_PASSWORD_AGE, &allow)) allow = 0; + /* in normal cases, just calculate it from policy */ return sampass->pass_last_set_time + allow; } +/* we need this for loading from the backend, so that we don't overwrite + non-changed max times, otherwise the pass_can_change checking won't work */ +time_t pdb_get_pass_can_change_time_noncalc(const struct samu *sampass) +{ + return sampass->pass_can_change_time; +} + time_t pdb_get_pass_must_change_time(const struct samu *sampass) { uint32 expire; @@ -100,6 +119,14 @@ time_t pdb_get_pass_must_change_time(const struct samu *sampass) return sampass->pass_last_set_time + expire; } +BOOL pdb_get_pass_can_change(const struct samu *sampass) +{ + if (sampass->pass_can_change_time == get_time_t_max() && + sampass->pass_last_set_time != 0) + return False; + return True; +} + uint16 pdb_get_logon_divs(const struct samu *sampass) { return sampass->logon_divs; @@ -944,43 +971,14 @@ BOOL pdb_set_backend_private_data(struct samu *sampass, void *private_data, /* Helpful interfaces to the above */ -/********************************************************************* - Sets the last changed times and must change times for a normal - password change. - ********************************************************************/ - -BOOL pdb_set_pass_changed_now(struct samu *sampass) +BOOL pdb_set_pass_can_change(struct samu *sampass, BOOL canchange) { - uint32 expire; - uint32 min_age; - - if (!pdb_set_pass_last_set_time (sampass, time(NULL), PDB_CHANGED)) - return False; - - if (!pdb_get_account_policy(AP_MAX_PASSWORD_AGE, &expire) - || (expire==(uint32)-1) || (expire == 0)) { - if (!pdb_set_pass_must_change_time (sampass, get_time_t_max(), PDB_CHANGED)) - return False; - } else { - if (!pdb_set_pass_must_change_time (sampass, - pdb_get_pass_last_set_time(sampass) - + expire, PDB_CHANGED)) - return False; - } - - if (!pdb_get_account_policy(AP_MIN_PASSWORD_AGE, &min_age) - || (min_age==(uint32)-1)) { - if (!pdb_set_pass_can_change_time (sampass, 0, PDB_CHANGED)) - return False; - } else { - if (!pdb_set_pass_can_change_time (sampass, - pdb_get_pass_last_set_time(sampass) - + min_age, PDB_CHANGED)) - return False; - } - return True; + return pdb_set_pass_can_change_time(sampass, + canchange ? 0 : get_time_t_max(), + PDB_CHANGED); } + /********************************************************************* Set the user's PLAINTEXT password. Used as an interface to the above. Also sets the last change time to NOW. @@ -1016,7 +1014,7 @@ BOOL pdb_set_plaintext_passwd(struct samu *sampass, const char *plaintext) if (!pdb_set_plaintext_pw_only (sampass, plaintext, PDB_CHANGED)) return False; - if (!pdb_set_pass_changed_now (sampass)) + if (!pdb_set_pass_last_set_time (sampass, time(NULL), PDB_CHANGED)) return False; /* Store the password history. */ diff --git a/source3/passdb/pdb_interface.c b/source3/passdb/pdb_interface.c index 7252ea4c8c..73f538214d 100644 --- a/source3/passdb/pdb_interface.c +++ b/source3/passdb/pdb_interface.c @@ -48,43 +48,6 @@ static BOOL lookup_global_sam_rid(TALLOC_CTX *mem_ctx, uint32 rid, const char **name, enum lsa_SidType *psid_name_use, union unid_t *unix_id); -/******************************************************************* - Clean up uninitialised passwords. The only way to tell - that these values are not 'real' is that they do not - have a valid last set time. Instead, the value is fixed at 0. - Therefore we use that as the key for 'is this a valid password'. - However, it is perfectly valid to have a 'default' last change - time, such LDAP with a missing attribute would produce. -********************************************************************/ - -static void pdb_force_pw_initialization(struct samu *pass) -{ - const uint8 *lm_pwd, *nt_pwd; - - /* only reset a password if the last set time has been - explicitly been set to zero. A default last set time - is ignored */ - - if ( (pdb_get_init_flags(pass, PDB_PASSLASTSET) != PDB_DEFAULT) - && (pdb_get_pass_last_set_time(pass) == 0) ) - { - - if (pdb_get_init_flags(pass, PDB_LMPASSWD) != PDB_DEFAULT) - { - lm_pwd = pdb_get_lanman_passwd(pass); - if (lm_pwd) - pdb_set_lanman_passwd(pass, NULL, PDB_CHANGED); - } - if (pdb_get_init_flags(pass, PDB_NTPASSWD) != PDB_DEFAULT) - { - nt_pwd = pdb_get_nt_passwd(pass); - if (nt_pwd) - pdb_set_nt_passwd(pass, NULL, PDB_CHANGED); - } - } - - return; -} NTSTATUS smb_register_passdb(int version, const char *name, pdb_init_function init) { @@ -250,7 +213,7 @@ BOOL pdb_getsampwent(struct samu *user) if ( !NT_STATUS_IS_OK(pdb->getsampwent(pdb, user) ) ) { return False; } - pdb_force_pw_initialization( user ); + return True; } @@ -266,8 +229,6 @@ BOOL pdb_getsampwnam(struct samu *sam_acct, const char *username) TALLOC_FREE(csamuser); } - pdb_force_pw_initialization( sam_acct ); - csamuser = samu_new( NULL ); if (!csamuser) { return False; diff --git a/source3/passdb/pdb_ldap.c b/source3/passdb/pdb_ldap.c index 0f03a1cc6e..a716dfa805 100644 --- a/source3/passdb/pdb_ldap.c +++ b/source3/passdb/pdb_ldap.c @@ -1096,7 +1096,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_KICKOFF_TIME), temp); - slprintf (temp, sizeof (temp) - 1, "%li", pdb_get_pass_can_change_time(sampass)); + slprintf (temp, sizeof (temp) - 1, "%li", pdb_get_pass_can_change_time_noncalc(sampass)); if (need_update(sampass, PDB_CANCHANGETIME)) smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods, get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_PWD_CAN_CHANGE), temp); diff --git a/source3/rpc_server/srv_netlog_nt.c b/source3/rpc_server/srv_netlog_nt.c index 6603d2f1d4..b8c776964e 100644 --- a/source3/rpc_server/srv_netlog_nt.c +++ b/source3/rpc_server/srv_netlog_nt.c @@ -623,7 +623,7 @@ NTSTATUS _net_srv_pwset(pipes_struct *p, NET_Q_SRV_PWSET *q_u, NET_R_SRV_PWSET * return NT_STATUS_NO_MEMORY; } - if (!pdb_set_pass_changed_now(sampass)) { + if (!pdb_set_pass_last_set_time(sampass, time(NULL), PDB_CHANGED)) { TALLOC_FREE(sampass); /* Not quite sure what this one qualifies as, but this will do */ return NT_STATUS_UNSUCCESSFUL; diff --git a/source3/rpc_server/srv_samr_nt.c b/source3/rpc_server/srv_samr_nt.c index 822a6a2ab7..5c0f50699e 100644 --- a/source3/rpc_server/srv_samr_nt.c +++ b/source3/rpc_server/srv_samr_nt.c @@ -40,6 +40,8 @@ ( READ_CONTROL_ACCESS | \ SA_RIGHT_USER_CHANGE_PASSWORD | \ SA_RIGHT_USER_SET_LOC_COM ) +#define SAMR_USR_RIGHTS_CANT_WRITE_PW \ + ( READ_CONTROL_ACCESS | SA_RIGHT_USER_SET_LOC_COM ) #define DISP_INFO_CACHE_TIMEOUT 10 @@ -90,6 +92,11 @@ static struct generic_mapping usr_generic_mapping = { GENERIC_RIGHTS_USER_WRITE, GENERIC_RIGHTS_USER_EXECUTE, GENERIC_RIGHTS_USER_ALL_ACCESS}; +static struct generic_mapping usr_nopwchange_generic_mapping = { + GENERIC_RIGHTS_USER_READ, + GENERIC_RIGHTS_USER_WRITE, + GENERIC_RIGHTS_USER_EXECUTE & ~SA_RIGHT_USER_CHANGE_PASSWORD, + GENERIC_RIGHTS_USER_ALL_ACCESS}; static struct generic_mapping grp_generic_mapping = { GENERIC_RIGHTS_GROUP_READ, GENERIC_RIGHTS_GROUP_WRITE, @@ -657,16 +664,6 @@ NTSTATUS _samr_get_usrdom_pwinfo(pipes_struct *p, SAMR_Q_GET_USRDOM_PWINFO *q_u, } /******************************************************************* - _samr_set_sec_obj - ********************************************************************/ - -NTSTATUS _samr_set_sec_obj(pipes_struct *p, SAMR_Q_SET_SEC_OBJ *q_u, SAMR_R_SET_SEC_OBJ *r_u) -{ - DEBUG(0,("_samr_set_sec_obj: Not yet implemented!\n")); - return NT_STATUS_NOT_IMPLEMENTED; -} - -/******************************************************************* ********************************************************************/ static BOOL get_lsa_policy_samr_sid( pipes_struct *p, POLICY_HND *pol, @@ -692,6 +689,97 @@ static BOOL get_lsa_policy_samr_sid( pipes_struct *p, POLICY_HND *pol, } /******************************************************************* + _samr_set_sec_obj + ********************************************************************/ + +NTSTATUS _samr_set_sec_obj(pipes_struct *p, SAMR_Q_SET_SEC_OBJ *q_u, SAMR_R_SET_SEC_OBJ *r_u) +{ + DOM_SID pol_sid; + uint32 acc_granted, i; + SEC_ACL *dacl; + BOOL ret; + struct samu *sampass=NULL; + NTSTATUS status; + + r_u->status = NT_STATUS_OK; + + if (!get_lsa_policy_samr_sid(p, &q_u->pol, &pol_sid, &acc_granted, NULL)) + return NT_STATUS_INVALID_HANDLE; + + if (!(sampass = samu_new( p->mem_ctx))) { + DEBUG(0,("No memory!\n")); + return NT_STATUS_NO_MEMORY; + } + + /* get the user record */ + become_root(); + ret = pdb_getsampwsid(sampass, &pol_sid); + unbecome_root(); + + if (!ret) { + DEBUG(4, ("User %s not found\n", sid_string_static(&pol_sid))); + TALLOC_FREE(sampass); + return NT_STATUS_INVALID_HANDLE; + } + + dacl = q_u->buf->sd->dacl; + for (i=0; i < dacl->num_aces; i++) { + if (sid_equal(&pol_sid, &dacl->aces[i].trustee)) { + ret = pdb_set_pass_can_change(sampass, + (dacl->aces[i].access_mask & + SA_RIGHT_USER_CHANGE_PASSWORD) ? + True: False); + break; + } + } + + if (!ret) { + TALLOC_FREE(sampass); + return NT_STATUS_ACCESS_DENIED; + } + + status = pdb_update_sam_account(sampass); + + TALLOC_FREE(sampass); + + return status; +} + +/******************************************************************* + build correct perms based on policies and password times for _samr_query_sec_obj +*******************************************************************/ +static BOOL check_change_pw_access(TALLOC_CTX *mem_ctx, DOM_SID *user_sid) +{ + struct samu *sampass=NULL; + BOOL ret; + + if ( !(sampass = samu_new( mem_ctx )) ) { + DEBUG(0,("No memory!\n")); + return False; + } + + become_root(); + ret = pdb_getsampwsid(sampass, user_sid); + unbecome_root(); + + if (ret == False) { + DEBUG(4,("User %s not found\n", sid_string_static(user_sid))); + TALLOC_FREE(sampass); + return False; + } + + DEBUG(3,("User:[%s]\n", pdb_get_username(sampass) )); + + if (pdb_get_pass_can_change(sampass)) { + TALLOC_FREE(sampass); + return True; + } + TALLOC_FREE(sampass); + return False; +} + + +/******************************************************************* _samr_query_sec_obj ********************************************************************/ @@ -731,7 +819,13 @@ NTSTATUS _samr_query_sec_obj(pipes_struct *p, SAMR_Q_QUERY_SEC_OBJ *q_u, SAMR_R_ /* TODO: different SDs have to be generated for aliases groups and users. Currently all three get a default user SD */ DEBUG(10,("_samr_query_sec_obj: querying security on Object with SID: %s\n", sid_to_string(str_sid, &pol_sid))); - r_u->status = make_samr_object_sd(p->mem_ctx, &psd, &sd_size, &usr_generic_mapping, &pol_sid, SAMR_USR_RIGHTS_WRITE_PW); + if (check_change_pw_access(p->mem_ctx, &pol_sid)) { + r_u->status = make_samr_object_sd(p->mem_ctx, &psd, &sd_size, &usr_generic_mapping, + &pol_sid, SAMR_USR_RIGHTS_WRITE_PW); + } else { + r_u->status = make_samr_object_sd(p->mem_ctx, &psd, &sd_size, &usr_nopwchange_generic_mapping, + &pol_sid, SAMR_USR_RIGHTS_CANT_WRITE_PW); + } } else { return NT_STATUS_OBJECT_TYPE_MISMATCH; } @@ -3056,7 +3150,7 @@ static BOOL set_user_info_18(SAM_USER_INFO_18 *id18, struct samu *pwd) TALLOC_FREE(pwd); return False; } - if (!pdb_set_pass_changed_now (pwd)) { + if (!pdb_set_pass_last_set_time (pwd, time(NULL), PDB_CHANGED)) { TALLOC_FREE(pwd); return False; } diff --git a/source3/smbd/chgpasswd.c b/source3/smbd/chgpasswd.c index cd847240dd..0b8dbfb492 100644 --- a/source3/smbd/chgpasswd.c +++ b/source3/smbd/chgpasswd.c @@ -689,7 +689,7 @@ BOOL change_lanman_password(struct samu *sampass, uchar *pass2) return False; /* We lose the NT hash. Sorry. */ } - if (!pdb_set_pass_changed_now (sampass)) { + if (!pdb_set_pass_last_set_time (sampass, time(NULL), PDB_CHANGED)) { TALLOC_FREE(sampass); /* Not quite sure what this one qualifies as, but this will do */ return False; @@ -1018,41 +1018,34 @@ static BOOL check_passwd_history(struct samu *sampass, const char *plaintext) NTSTATUS change_oem_password(struct samu *hnd, char *old_passwd, char *new_passwd, BOOL as_root, uint32 *samr_reject_reason) { - uint32 min_len, min_age; + uint32 min_len; struct passwd *pass = NULL; const char *username = pdb_get_username(hnd); - time_t last_change_time = pdb_get_pass_last_set_time(hnd); time_t can_change_time = pdb_get_pass_can_change_time(hnd); if (samr_reject_reason) { *samr_reject_reason = Undefined; } - if (pdb_get_account_policy(AP_MIN_PASSWORD_AGE, &min_age)) { - /* - * Windows calculates the minimum password age check - * dynamically, it basically ignores the pwdcanchange - * timestamp. Do likewise. - */ - if (last_change_time + min_age > time(NULL)) { - DEBUG(1, ("user %s cannot change password now, must " - "wait until %s\n", username, - http_timestring(last_change_time+min_age))); - if (samr_reject_reason) { - *samr_reject_reason = REJECT_REASON_OTHER; - } - return NT_STATUS_ACCOUNT_RESTRICTION; + /* check to see if the secdesc has previously been set to disallow */ + if (!pdb_get_pass_can_change(hnd)) { + DEBUG(1, ("user %s does not have permissions to change password\n")); + if (samr_reject_reason) { + *samr_reject_reason = REJECT_REASON_OTHER; } - } else { - if ((can_change_time != 0) && (time(NULL) < can_change_time)) { - DEBUG(1, ("user %s cannot change password now, must " - "wait until %s\n", username, - http_timestring(can_change_time))); - if (samr_reject_reason) { - *samr_reject_reason = REJECT_REASON_OTHER; - } - return NT_STATUS_ACCOUNT_RESTRICTION; + return NT_STATUS_ACCOUNT_RESTRICTION; + } + + /* removed calculation here, becuase passdb now calculates + based on policy. jmcd */ + if ((can_change_time != 0) && (time(NULL) < can_change_time)) { + DEBUG(1, ("user %s cannot change password now, must " + "wait until %s\n", username, + http_timestring(can_change_time))); + if (samr_reject_reason) { + *samr_reject_reason = REJECT_REASON_OTHER; } + return NT_STATUS_ACCOUNT_RESTRICTION; } if (pdb_get_account_policy(AP_MIN_PASSWORD_LEN, &min_len) && (str_charnum(new_passwd) < min_len)) { |