pw_mgmt.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441
  1. /** BEGIN COPYRIGHT BLOCK
  2. * This Program is free software; you can redistribute it and/or modify it under
  3. * the terms of the GNU General Public License as published by the Free Software
  4. * Foundation; version 2 of the License.
  5. *
  6. * This Program is distributed in the hope that it will be useful, but WITHOUT
  7. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  8. * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  9. *
  10. * You should have received a copy of the GNU General Public License along with
  11. * this Program; if not, write to the Free Software Foundation, Inc., 59 Temple
  12. * Place, Suite 330, Boston, MA 02111-1307 USA.
  13. *
  14. * In addition, as a special exception, Red Hat, Inc. gives You the additional
  15. * right to link the code of this Program with code not covered under the GNU
  16. * General Public License ("Non-GPL Code") and to distribute linked combinations
  17. * including the two, subject to the limitations in this paragraph. Non-GPL Code
  18. * permitted under this exception must only link to the code of this Program
  19. * through those well defined interfaces identified in the file named EXCEPTION
  20. * found in the source code files (the "Approved Interfaces"). The files of
  21. * Non-GPL Code may instantiate templates or use macros or inline functions from
  22. * the Approved Interfaces without causing the resulting work to be covered by
  23. * the GNU General Public License. Only Red Hat, Inc. may make changes or
  24. * additions to the list of Approved Interfaces. You must obey the GNU General
  25. * Public License in all respects for all of the Program code and other code used
  26. * in conjunction with the Program except the Non-GPL Code covered by this
  27. * exception. If you modify this file, you may extend this exception to your
  28. * version of the file, but you are not obligated to do so. If you do not wish to
  29. * provide this exception without modification, you must delete this exception
  30. * statement from your version and license this file solely under the GPL without
  31. * exception.
  32. *
  33. *
  34. * Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
  35. * Copyright (C) 2005 Red Hat, Inc.
  36. * All rights reserved.
  37. * END COPYRIGHT BLOCK **/
  38. #ifdef HAVE_CONFIG_H
  39. # include <config.h>
  40. #endif
  41. /* pw_mgmt.c
  42. */
  43. #include <time.h>
  44. #include <string.h>
  45. #include "slap.h"
  46. /****************************************************************************/
  47. /* prototypes */
  48. /****************************************************************************/
  49. /* need_new_pw() is called when non rootdn bind operation succeeds with authentication */
  50. int
  51. need_new_pw( Slapi_PBlock *pb, long *t, Slapi_Entry *e, int pwresponse_req )
  52. {
  53. time_t cur_time, pw_exp_date;
  54. LDAPMod *mod;
  55. Slapi_Mods smods;
  56. double diff_t = 0;
  57. char *cur_time_str = NULL;
  58. char *passwordExpirationTime;
  59. char *timestring;
  60. char *dn;
  61. passwdPolicy *pwpolicy = NULL;
  62. int pwdGraceUserTime = 0;
  63. char graceUserTime[8];
  64. slapi_mods_init (&smods, 0);
  65. dn = slapi_entry_get_ndn( e );
  66. pwpolicy = new_passwdPolicy(pb, dn);
  67. /* after the user binds with authentication, clear the retry count */
  68. if ( pwpolicy->pw_lockout == 1)
  69. {
  70. if(slapi_entry_attr_get_int( e, "passwordRetryCount") > 0)
  71. {
  72. slapi_mods_add_string(&smods, LDAP_MOD_REPLACE, "passwordRetryCount", "0");
  73. }
  74. }
  75. cur_time = current_time();
  76. /* get passwordExpirationTime attribute */
  77. passwordExpirationTime= slapi_entry_attr_get_charptr(e, "passwordExpirationTime");
  78. if (passwordExpirationTime == NULL)
  79. {
  80. /* password expiration date is not set.
  81. * This is ok for data that has been loaded via ldif2ldbm
  82. * Set expiration time if needed,
  83. * don't do further checking and return 0 */
  84. if ( pwpolicy->pw_exp == 1) {
  85. pw_exp_date = time_plus_sec ( cur_time,
  86. pwpolicy->pw_maxage );
  87. timestring = format_genTime (pw_exp_date);
  88. slapi_mods_add_string(&smods, LDAP_MOD_REPLACE, "passwordExpirationTime", timestring);
  89. slapi_ch_free((void **)&timestring);
  90. slapi_mods_add_string(&smods, LDAP_MOD_REPLACE, "passwordExpWarned", "0");
  91. pw_apply_mods(dn, &smods);
  92. } else if (pwpolicy->pw_lockout == 1) {
  93. pw_apply_mods(dn, &smods);
  94. }
  95. slapi_mods_done(&smods);
  96. delete_passwdPolicy(&pwpolicy);
  97. return ( 0 );
  98. }
  99. pw_exp_date = parse_genTime(passwordExpirationTime);
  100. slapi_ch_free((void**)&passwordExpirationTime);
  101. /* Check if password has been reset */
  102. if ( pw_exp_date == NO_TIME ) {
  103. /* check if changing password is required */
  104. if ( pwpolicy->pw_must_change ) {
  105. /* set c_needpw for this connection to be true. this client
  106. now can only change its own password */
  107. pb->pb_conn->c_needpw = 1;
  108. *t=0;
  109. /* We need to include "changeafterreset" error in
  110. * passwordpolicy response control. So, we will not be
  111. * done here. We remember this scenario by (c_needpw=1)
  112. * and check it before sending the control from various
  113. * places. We will also add LDAP_CONTROL_PWEXPIRED control
  114. * as the return value used to be (1).
  115. */
  116. goto skip;
  117. }
  118. /* Mark that first login occured */
  119. pw_exp_date = NOT_FIRST_TIME;
  120. timestring = format_genTime(pw_exp_date);
  121. slapi_mods_add_string(&smods, LDAP_MOD_REPLACE, "passwordExpirationTime", timestring);
  122. slapi_ch_free((void **)&timestring);
  123. }
  124. skip:
  125. /* if password never expires, don't need to go on; return 0 */
  126. if ( pwpolicy->pw_exp == 0 ) {
  127. /* check for "changeafterreset" condition */
  128. if (pb->pb_conn->c_needpw == 1) {
  129. if (pwresponse_req) {
  130. slapi_pwpolicy_make_response_control ( pb, -1, -1, LDAP_PWPOLICY_CHGAFTERRESET );
  131. }
  132. slapi_add_pwd_control ( pb, LDAP_CONTROL_PWEXPIRED, 0);
  133. }
  134. pw_apply_mods(dn, &smods);
  135. slapi_mods_done(&smods);
  136. delete_passwdPolicy(&pwpolicy);
  137. return ( 0 );
  138. }
  139. /* check if password expired. If so, abort bind. */
  140. cur_time_str = format_genTime ( cur_time );
  141. if ( pw_exp_date != NO_TIME &&
  142. pw_exp_date != NOT_FIRST_TIME &&
  143. (diff_t = difftime ( pw_exp_date,
  144. parse_genTime ( cur_time_str ))) <= 0 ) {
  145. slapi_ch_free_string(&cur_time_str); /* only need this above */
  146. /* password has expired. Check the value of
  147. * passwordGraceUserTime and compare it
  148. * against the value of passwordGraceLimit */
  149. pwdGraceUserTime = slapi_entry_attr_get_int( e, "passwordGraceUserTime");
  150. if ( pwpolicy->pw_gracelimit > pwdGraceUserTime ) {
  151. pwdGraceUserTime++;
  152. sprintf ( graceUserTime, "%d", pwdGraceUserTime );
  153. slapi_mods_add_string(&smods, LDAP_MOD_REPLACE,
  154. "passwordGraceUserTime", graceUserTime);
  155. pw_apply_mods(dn, &smods);
  156. slapi_mods_done(&smods);
  157. if (pwresponse_req) {
  158. /* check for "changeafterreset" condition */
  159. if (pb->pb_conn->c_needpw == 1) {
  160. slapi_pwpolicy_make_response_control( pb, -1,
  161. ((pwpolicy->pw_gracelimit) - pwdGraceUserTime),
  162. LDAP_PWPOLICY_CHGAFTERRESET);
  163. } else {
  164. slapi_pwpolicy_make_response_control( pb, -1,
  165. ((pwpolicy->pw_gracelimit) - pwdGraceUserTime),
  166. -1);
  167. }
  168. }
  169. if (pb->pb_conn->c_needpw == 1) {
  170. slapi_add_pwd_control ( pb, LDAP_CONTROL_PWEXPIRED, 0);
  171. }
  172. delete_passwdPolicy(&pwpolicy);
  173. return ( 0 );
  174. }
  175. /* password expired and user exceeded limit of grace attemps.
  176. * Send result and also the control */
  177. slapi_add_pwd_control ( pb, LDAP_CONTROL_PWEXPIRED, 0);
  178. if (pwresponse_req) {
  179. slapi_pwpolicy_make_response_control ( pb, -1, -1, LDAP_PWPOLICY_PWDEXPIRED );
  180. }
  181. slapi_send_ldap_result ( pb, LDAP_INVALID_CREDENTIALS, NULL,
  182. "password expired!", 0, NULL );
  183. /* abort bind */
  184. /* pass pb to do_unbind(). pb->pb_op->o_opid and
  185. pb->pb_op->o_tag are not right but I don't see
  186. do_unbind() checking for those. We might need to
  187. create a pb for unbind operation. Also do_unbind calls
  188. pre and post ops. Maybe we don't want to call them */
  189. if (pb->pb_conn && (LDAP_VERSION2 == pb->pb_conn->c_ldapversion)) {
  190. /* We close the connection only with LDAPv2 connections */
  191. do_unbind( pb );
  192. }
  193. /* Apply current modifications */
  194. pw_apply_mods(dn, &smods);
  195. slapi_mods_done(&smods);
  196. delete_passwdPolicy(&pwpolicy);
  197. return (-1);
  198. }
  199. slapi_ch_free((void **) &cur_time_str );
  200. /* check if password is going to expire within "passwordWarning" */
  201. /* Note that if pw_exp_date is NO_TIME or NOT_FIRST_TIME,
  202. * we must send warning first and this changes the expiration time.
  203. * This is done just below since diff_t is 0
  204. */
  205. if ( diff_t <= pwpolicy->pw_warning ) {
  206. int pw_exp_warned = 0;
  207. pw_exp_warned= slapi_entry_attr_get_int( e, "passwordExpWarned");
  208. if ( !pw_exp_warned ){
  209. /* first time send out a warning */
  210. /* reset the expiration time to current + warning time
  211. * and set passwordExpWarned to true
  212. */
  213. if (pb->pb_conn->c_needpw != 1) {
  214. pw_exp_date = time_plus_sec ( cur_time,
  215. pwpolicy->pw_warning );
  216. }
  217. timestring = format_genTime(pw_exp_date);
  218. /* At this time passwordExpirationTime may already be
  219. * in the list of mods: Remove it */
  220. for (mod = slapi_mods_get_first_mod(&smods); mod != NULL;
  221. mod = slapi_mods_get_next_mod(&smods))
  222. {
  223. if (!strcmp(mod->mod_type, "passwordExpirationTime"))
  224. slapi_mods_remove(&smods);
  225. }
  226. slapi_mods_add_string(&smods, LDAP_MOD_REPLACE, "passwordExpirationTime", timestring);
  227. slapi_ch_free((void **)&timestring);
  228. slapi_mods_add_string(&smods, LDAP_MOD_REPLACE, "passwordExpWarned", "1");
  229. *t = pwpolicy->pw_warning;
  230. } else {
  231. *t = (long)diff_t; /* jcm: had to cast double to long */
  232. }
  233. pw_apply_mods(dn, &smods);
  234. slapi_mods_done(&smods);
  235. if (pwresponse_req) {
  236. /* check for "changeafterreset" condition */
  237. if (pb->pb_conn->c_needpw == 1) {
  238. slapi_pwpolicy_make_response_control( pb, *t, -1,
  239. LDAP_PWPOLICY_CHGAFTERRESET);
  240. } else {
  241. slapi_pwpolicy_make_response_control( pb, *t, -1,
  242. -1);
  243. }
  244. }
  245. if (pb->pb_conn->c_needpw == 1) {
  246. slapi_add_pwd_control ( pb, LDAP_CONTROL_PWEXPIRED, 0);
  247. }
  248. delete_passwdPolicy(&pwpolicy);
  249. return (2);
  250. }
  251. pw_apply_mods(dn, &smods);
  252. slapi_mods_done(&smods);
  253. /* Leftover from "changeafterreset" condition */
  254. if (pb->pb_conn->c_needpw == 1) {
  255. slapi_add_pwd_control ( pb, LDAP_CONTROL_PWEXPIRED, 0);
  256. }
  257. delete_passwdPolicy(&pwpolicy);
  258. /* passes checking, return 0 */
  259. return( 0 );
  260. }
  261. /* check_account_lock is called before bind opeation; this could be a pre-op. */
  262. int
  263. check_account_lock ( Slapi_PBlock *pb, Slapi_Entry * bind_target_entry, int pwresponse_req, int account_inactivation_only) {
  264. time_t unlock_time;
  265. time_t cur_time;
  266. char *cur_time_str = NULL;
  267. char *accountUnlockTime;
  268. passwdPolicy *pwpolicy = NULL;
  269. char *dn = NULL;
  270. /* kexcoff: account inactivation */
  271. int rc = 0;
  272. Slapi_ValueSet *values = NULL;
  273. int type_name_disposition = 0;
  274. char *actual_type_name = NULL;
  275. int attr_free_flags = 0;
  276. /* kexcoff - end */
  277. if ( bind_target_entry == NULL )
  278. return -1;
  279. if(!account_inactivation_only)
  280. {
  281. dn = slapi_entry_get_ndn(bind_target_entry);
  282. pwpolicy = new_passwdPolicy(pb, dn);
  283. }
  284. /* kexcoff: account inactivation */
  285. /* check if the entry is locked by nsAccountLock attribute - account inactivation feature */
  286. rc = slapi_vattr_values_get(bind_target_entry, "nsAccountLock",
  287. &values,
  288. &type_name_disposition, &actual_type_name,
  289. SLAPI_VIRTUALATTRS_REQUEST_POINTERS,
  290. &attr_free_flags);
  291. if ( rc == 0 )
  292. {
  293. Slapi_Value *v = NULL;
  294. const struct berval *bvp = NULL;
  295. if ( (slapi_valueset_first_value( values, &v ) != -1) &&
  296. ( bvp = slapi_value_get_berval( v )) != NULL )
  297. {
  298. if ( (bvp != NULL) && (strcasecmp(bvp->bv_val, "true") == 0) )
  299. {
  300. /* account inactivated */
  301. if (!account_inactivation_only && pwresponse_req) {
  302. slapi_pwpolicy_make_response_control ( pb, -1, -1,
  303. LDAP_PWPOLICY_ACCTLOCKED );
  304. }
  305. if(!account_inactivation_only)
  306. send_ldap_result ( pb, LDAP_UNWILLING_TO_PERFORM, NULL,
  307. "Account inactivated. Contact system administrator.",
  308. 0, NULL );
  309. slapi_vattr_values_free(&values, &actual_type_name, attr_free_flags);
  310. goto locked;
  311. }
  312. } /* else, account "activated", keep on the process */
  313. if ( values != NULL )
  314. slapi_vattr_values_free(&values, &actual_type_name, attr_free_flags);
  315. }
  316. /* kexcoff - end */
  317. /*
  318. * Check if the password policy has to be checked or not
  319. */
  320. if ( account_inactivation_only || pwpolicy->pw_lockout == 0 ) {
  321. goto notlocked;
  322. }
  323. /*
  324. * Check the attribute of the password policy
  325. */
  326. /* check if account is locked out. If so, send result and return 1 */
  327. {
  328. unsigned int maxfailure= pwpolicy->pw_maxfailure;
  329. /* It's locked if passwordRetryCount >= maxfailure */
  330. if ( slapi_entry_attr_get_uint(bind_target_entry,"passwordRetryCount") < maxfailure )
  331. {
  332. /* Not locked */
  333. goto notlocked;
  334. }
  335. }
  336. /* locked but maybe it's time to unlock it */
  337. accountUnlockTime= slapi_entry_attr_get_charptr(bind_target_entry, "accountUnlockTime");
  338. if (accountUnlockTime != NULL)
  339. {
  340. unlock_time = parse_genTime(accountUnlockTime);
  341. slapi_ch_free((void **) &accountUnlockTime );
  342. if ( pwpolicy->pw_unlock == 0 &&
  343. unlock_time == NO_TIME ) {
  344. /* account is locked forever. contact admin to reset */
  345. if (pwresponse_req) {
  346. slapi_pwpolicy_make_response_control ( pb, -1, -1,
  347. LDAP_PWPOLICY_ACCTLOCKED );
  348. }
  349. send_ldap_result ( pb, LDAP_CONSTRAINT_VIOLATION, NULL,
  350. "Exceed password retry limit. Contact system administrator to reset."
  351. , 0, NULL );
  352. goto locked;
  353. }
  354. cur_time = current_time();
  355. cur_time_str = format_genTime( cur_time);
  356. if ( difftime ( parse_genTime( cur_time_str ), unlock_time ) < 0 ) {
  357. /* account is locked, cannot do anything */
  358. if (pwresponse_req) {
  359. slapi_pwpolicy_make_response_control ( pb, -1, -1,
  360. LDAP_PWPOLICY_ACCTLOCKED );
  361. }
  362. send_ldap_result ( pb, LDAP_CONSTRAINT_VIOLATION, NULL,
  363. "Exceed password retry limit. Please try later." , 0, NULL );
  364. slapi_ch_free((void **) &cur_time_str );
  365. goto locked;
  366. }
  367. slapi_ch_free((void **) &cur_time_str );
  368. }
  369. notlocked:
  370. /* account is not locked. */
  371. if(!account_inactivation_only)
  372. delete_passwdPolicy(&pwpolicy);
  373. return ( 0 );
  374. locked:
  375. if(!account_inactivation_only)
  376. delete_passwdPolicy(&pwpolicy);
  377. return (1);
  378. }
  379. void
  380. pw_init ( void ) {
  381. slapdFrontendConfig_t *slapdFrontendConfig;
  382. pw_set_componentID(generate_componentid(NULL, COMPONENT_PWPOLICY));
  383. slapdFrontendConfig = getFrontendConfig();
  384. pw_mod_allowchange_aci (!slapdFrontendConfig->pw_policy.pw_change &&
  385. !slapdFrontendConfig->pw_policy.pw_must_change);
  386. }