pw_mgmt.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  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. const Slapi_DN *sdn;
  62. passwdPolicy *pwpolicy = NULL;
  63. int pwdGraceUserTime = 0;
  64. char graceUserTime[8];
  65. slapi_mods_init (&smods, 0);
  66. sdn = slapi_entry_get_sdn_const( e );
  67. dn = slapi_entry_get_ndn( e );
  68. pwpolicy = new_passwdPolicy(pb, dn);
  69. /* after the user binds with authentication, clear the retry count */
  70. if ( pwpolicy->pw_lockout == 1)
  71. {
  72. if(slapi_entry_attr_get_int( e, "passwordRetryCount") > 0)
  73. {
  74. slapi_mods_add_string(&smods, LDAP_MOD_REPLACE, "passwordRetryCount", "0");
  75. }
  76. }
  77. cur_time = current_time();
  78. /* get passwordExpirationTime attribute */
  79. passwordExpirationTime= slapi_entry_attr_get_charptr(e, "passwordExpirationTime");
  80. if (passwordExpirationTime == NULL)
  81. {
  82. /* password expiration date is not set.
  83. * This is ok for data that has been loaded via ldif2ldbm
  84. * Set expiration time if needed,
  85. * don't do further checking and return 0 */
  86. if ( pwpolicy->pw_exp == 1) {
  87. pw_exp_date = time_plus_sec ( cur_time,
  88. pwpolicy->pw_maxage );
  89. timestring = format_genTime (pw_exp_date);
  90. slapi_mods_add_string(&smods, LDAP_MOD_REPLACE, "passwordExpirationTime", timestring);
  91. slapi_ch_free((void **)&timestring);
  92. slapi_mods_add_string(&smods, LDAP_MOD_REPLACE, "passwordExpWarned", "0");
  93. pw_apply_mods(sdn, &smods);
  94. } else if (pwpolicy->pw_lockout == 1) {
  95. pw_apply_mods(sdn, &smods);
  96. }
  97. slapi_mods_done(&smods);
  98. delete_passwdPolicy(&pwpolicy);
  99. return ( 0 );
  100. }
  101. pw_exp_date = parse_genTime(passwordExpirationTime);
  102. slapi_ch_free((void**)&passwordExpirationTime);
  103. /* Check if password has been reset */
  104. if ( pw_exp_date == NO_TIME ) {
  105. /* check if changing password is required */
  106. if ( pwpolicy->pw_must_change ) {
  107. /* set c_needpw for this connection to be true. this client
  108. now can only change its own password */
  109. pb->pb_conn->c_needpw = 1;
  110. *t=0;
  111. /* We need to include "changeafterreset" error in
  112. * passwordpolicy response control. So, we will not be
  113. * done here. We remember this scenario by (c_needpw=1)
  114. * and check it before sending the control from various
  115. * places. We will also add LDAP_CONTROL_PWEXPIRED control
  116. * as the return value used to be (1).
  117. */
  118. goto skip;
  119. }
  120. /* Mark that first login occured */
  121. pw_exp_date = NOT_FIRST_TIME;
  122. timestring = format_genTime(pw_exp_date);
  123. slapi_mods_add_string(&smods, LDAP_MOD_REPLACE, "passwordExpirationTime", timestring);
  124. slapi_ch_free((void **)&timestring);
  125. }
  126. skip:
  127. /* if password never expires, don't need to go on; return 0 */
  128. if ( pwpolicy->pw_exp == 0 ) {
  129. /* check for "changeafterreset" condition */
  130. if (pb->pb_conn->c_needpw == 1) {
  131. if (pwresponse_req) {
  132. slapi_pwpolicy_make_response_control ( pb, -1, -1, LDAP_PWPOLICY_CHGAFTERRESET );
  133. }
  134. slapi_add_pwd_control ( pb, LDAP_CONTROL_PWEXPIRED, 0);
  135. }
  136. pw_apply_mods(sdn, &smods);
  137. slapi_mods_done(&smods);
  138. delete_passwdPolicy(&pwpolicy);
  139. return ( 0 );
  140. }
  141. /* check if password expired. If so, abort bind. */
  142. cur_time_str = format_genTime ( cur_time );
  143. if ( pw_exp_date != NO_TIME &&
  144. pw_exp_date != NOT_FIRST_TIME &&
  145. (diff_t = difftime ( pw_exp_date,
  146. parse_genTime ( cur_time_str ))) <= 0 ) {
  147. slapi_ch_free_string(&cur_time_str); /* only need this above */
  148. /* password has expired. Check the value of
  149. * passwordGraceUserTime and compare it
  150. * against the value of passwordGraceLimit */
  151. pwdGraceUserTime = slapi_entry_attr_get_int( e, "passwordGraceUserTime");
  152. if ( pwpolicy->pw_gracelimit > pwdGraceUserTime ) {
  153. pwdGraceUserTime++;
  154. sprintf ( graceUserTime, "%d", pwdGraceUserTime );
  155. slapi_mods_add_string(&smods, LDAP_MOD_REPLACE,
  156. "passwordGraceUserTime", graceUserTime);
  157. pw_apply_mods(sdn, &smods);
  158. slapi_mods_done(&smods);
  159. if (pwresponse_req) {
  160. /* check for "changeafterreset" condition */
  161. if (pb->pb_conn->c_needpw == 1) {
  162. slapi_pwpolicy_make_response_control( pb, -1,
  163. ((pwpolicy->pw_gracelimit) - pwdGraceUserTime),
  164. LDAP_PWPOLICY_CHGAFTERRESET);
  165. } else {
  166. slapi_pwpolicy_make_response_control( pb, -1,
  167. ((pwpolicy->pw_gracelimit) - pwdGraceUserTime),
  168. -1);
  169. }
  170. }
  171. if (pb->pb_conn->c_needpw == 1) {
  172. slapi_add_pwd_control ( pb, LDAP_CONTROL_PWEXPIRED, 0);
  173. }
  174. delete_passwdPolicy(&pwpolicy);
  175. return ( 0 );
  176. }
  177. /* password expired and user exceeded limit of grace attemps.
  178. * Send result and also the control */
  179. slapi_add_pwd_control ( pb, LDAP_CONTROL_PWEXPIRED, 0);
  180. if (pwresponse_req) {
  181. slapi_pwpolicy_make_response_control ( pb, -1, -1, LDAP_PWPOLICY_PWDEXPIRED );
  182. }
  183. slapi_send_ldap_result ( pb, LDAP_INVALID_CREDENTIALS, NULL,
  184. "password expired!", 0, NULL );
  185. /* abort bind */
  186. /* pass pb to do_unbind(). pb->pb_op->o_opid and
  187. pb->pb_op->o_tag are not right but I don't see
  188. do_unbind() checking for those. We might need to
  189. create a pb for unbind operation. Also do_unbind calls
  190. pre and post ops. Maybe we don't want to call them */
  191. if (pb->pb_conn && (LDAP_VERSION2 == pb->pb_conn->c_ldapversion)) {
  192. /* We close the connection only with LDAPv2 connections */
  193. disconnect_server( pb->pb_conn, pb->pb_op->o_connid,
  194. pb->pb_op->o_opid, SLAPD_DISCONNECT_UNBIND, 0);
  195. }
  196. /* Apply current modifications */
  197. pw_apply_mods(sdn, &smods);
  198. slapi_mods_done(&smods);
  199. delete_passwdPolicy(&pwpolicy);
  200. return (-1);
  201. }
  202. slapi_ch_free((void **) &cur_time_str );
  203. /* check if password is going to expire within "passwordWarning" */
  204. /* Note that if pw_exp_date is NO_TIME or NOT_FIRST_TIME,
  205. * we must send warning first and this changes the expiration time.
  206. * This is done just below since diff_t is 0
  207. */
  208. if ( diff_t <= pwpolicy->pw_warning ) {
  209. int pw_exp_warned = 0;
  210. pw_exp_warned= slapi_entry_attr_get_int( e, "passwordExpWarned");
  211. if ( !pw_exp_warned ){
  212. /* first time send out a warning */
  213. /* reset the expiration time to current + warning time
  214. * and set passwordExpWarned to true
  215. */
  216. if (pb->pb_conn->c_needpw != 1) {
  217. pw_exp_date = time_plus_sec ( cur_time,
  218. pwpolicy->pw_warning );
  219. }
  220. timestring = format_genTime(pw_exp_date);
  221. /* At this time passwordExpirationTime may already be
  222. * in the list of mods: Remove it */
  223. for (mod = slapi_mods_get_first_mod(&smods); mod != NULL;
  224. mod = slapi_mods_get_next_mod(&smods))
  225. {
  226. if (!strcmp(mod->mod_type, "passwordExpirationTime"))
  227. slapi_mods_remove(&smods);
  228. }
  229. slapi_mods_add_string(&smods, LDAP_MOD_REPLACE, "passwordExpirationTime", timestring);
  230. slapi_ch_free((void **)&timestring);
  231. slapi_mods_add_string(&smods, LDAP_MOD_REPLACE, "passwordExpWarned", "1");
  232. *t = pwpolicy->pw_warning;
  233. } else {
  234. *t = (long)diff_t; /* jcm: had to cast double to long */
  235. }
  236. pw_apply_mods(sdn, &smods);
  237. slapi_mods_done(&smods);
  238. if (pwresponse_req) {
  239. /* check for "changeafterreset" condition */
  240. if (pb->pb_conn->c_needpw == 1) {
  241. slapi_pwpolicy_make_response_control( pb, *t, -1,
  242. LDAP_PWPOLICY_CHGAFTERRESET);
  243. } else {
  244. slapi_pwpolicy_make_response_control( pb, *t, -1,
  245. -1);
  246. }
  247. }
  248. if (pb->pb_conn->c_needpw == 1) {
  249. slapi_add_pwd_control ( pb, LDAP_CONTROL_PWEXPIRED, 0);
  250. }
  251. delete_passwdPolicy(&pwpolicy);
  252. return (2);
  253. }
  254. pw_apply_mods(sdn, &smods);
  255. slapi_mods_done(&smods);
  256. /* Leftover from "changeafterreset" condition */
  257. if (pb->pb_conn->c_needpw == 1) {
  258. slapi_add_pwd_control ( pb, LDAP_CONTROL_PWEXPIRED, 0);
  259. }
  260. delete_passwdPolicy(&pwpolicy);
  261. /* passes checking, return 0 */
  262. return( 0 );
  263. }
  264. void
  265. pw_init ( void ) {
  266. slapdFrontendConfig_t *slapdFrontendConfig;
  267. pw_set_componentID(generate_componentid(NULL, COMPONENT_PWPOLICY));
  268. slapdFrontendConfig = getFrontendConfig();
  269. pw_mod_allowchange_aci (!slapdFrontendConfig->pw_policy.pw_change &&
  270. !slapdFrontendConfig->pw_policy.pw_must_change);
  271. slapi_add_internal_attr_syntax( PSEUDO_ATTR_UNHASHEDUSERPASSWORD,
  272. PSEUDO_ATTR_UNHASHEDUSERPASSWORD_OID,
  273. OCTETSTRING_SYNTAX_OID, 0,
  274. /* Clients don't need to directly modify
  275. * PSEUDO_ATTR_UNHASHEDUSERPASSWORD */
  276. SLAPI_ATTR_FLAG_NOUSERMOD);
  277. }