acct_plugin.c 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. /******************************************************************************
  2. Copyright (C) 2009 Hewlett-Packard Development Company, L.P.
  3. This program is free software; you can redistribute it and/or
  4. modify it under the terms of the GNU General Public License
  5. version 2 as published by the Free Software Foundation.
  6. This program is distributed in the hope that it will be useful,
  7. but WITHOUT ANY WARRANTY; without even the implied warranty of
  8. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  9. GNU General Public License for more details.
  10. You should have received a copy of the GNU General Public License
  11. along with this program; if not, write to the Free Software
  12. Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  13. Contributors:
  14. Hewlett-Packard Development Company, L.P.
  15. ******************************************************************************/
  16. /* Account Policy plugin */
  17. #include <stdio.h>
  18. #include <string.h>
  19. #include <ctype.h>
  20. #include <time.h>
  21. #include "slapi-plugin.h"
  22. #include "acctpolicy.h"
  23. /*
  24. Checks bind entry for last login state and compares current time with last
  25. login time plus the limit to decide whether to deny the bind.
  26. */
  27. static int
  28. acct_inact_limit( Slapi_PBlock *pb, char *dn, Slapi_Entry *target_entry, acctPolicy *policy )
  29. {
  30. char *lasttimestr = NULL;
  31. time_t lim_t, last_t, cur_t;
  32. int rc = 0; /* Optimistic default */
  33. acctPluginCfg *cfg;
  34. cfg = get_config();
  35. if( ( lasttimestr = get_attr_string_val( target_entry,
  36. cfg->state_attr_name ) ) != NULL ) {
  37. slapi_log_error( SLAPI_LOG_PLUGIN, PRE_PLUGIN_NAME,
  38. "\"%s\" login timestamp is %s\n", dn, lasttimestr );
  39. } else if( ( lasttimestr = get_attr_string_val( target_entry,
  40. cfg->alt_state_attr_name ) ) != NULL ) {
  41. slapi_log_error( SLAPI_LOG_PLUGIN, PRE_PLUGIN_NAME,
  42. "\"%s\" alternate timestamp is %s\n", dn, lasttimestr );
  43. } else {
  44. slapi_log_error( SLAPI_LOG_PLUGIN, PRE_PLUGIN_NAME,
  45. "\"%s\" has no login or creation timestamp\n", dn );
  46. rc = -1;
  47. goto done;
  48. }
  49. last_t = gentimeToEpochtime( lasttimestr );
  50. cur_t = time( (time_t*)0 );
  51. lim_t = policy->inactivitylimit;
  52. /* Finally do the time comparison */
  53. if( cur_t > last_t + lim_t ) {
  54. slapi_log_error( SLAPI_LOG_PLUGIN, PRE_PLUGIN_NAME,
  55. "\"%s\" has exceeded inactivity limit (%ld > (%ld + %ld))\n",
  56. dn, cur_t, last_t, lim_t );
  57. rc = 1;
  58. goto done;
  59. } else {
  60. slapi_log_error( SLAPI_LOG_PLUGIN, PRE_PLUGIN_NAME,
  61. "\"%s\" is within inactivity limit (%ld < (%ld + %ld))\n",
  62. dn, cur_t, last_t, lim_t );
  63. }
  64. done:
  65. /* Deny bind; the account has exceeded the inactivity limit */
  66. if( rc == 1 ) {
  67. slapi_send_ldap_result( pb, LDAP_CONSTRAINT_VIOLATION, NULL,
  68. "Account inactivity limit exceeded."
  69. " Contact system administrator to reset.", 0, NULL );
  70. }
  71. slapi_ch_free_string( &lasttimestr );
  72. return( rc );
  73. }
  74. /*
  75. This is called after binds, it updates an attribute in the account
  76. with the current time.
  77. */
  78. static int
  79. acct_record_login( const char *dn )
  80. {
  81. int ldrc;
  82. int rc = 0; /* Optimistic default */
  83. LDAPMod *mods[2];
  84. LDAPMod mod;
  85. struct berval *vals[2];
  86. struct berval val;
  87. char *timestr = NULL;
  88. acctPluginCfg *cfg;
  89. void *plugin_id;
  90. Slapi_PBlock *modpb = NULL;
  91. cfg = get_config();
  92. plugin_id = get_identity();
  93. timestr = epochtimeToGentime( time( (time_t*)0 ) );
  94. val.bv_val = timestr;
  95. val.bv_len = strlen( val.bv_val );
  96. vals [0] = &val;
  97. vals [1] = NULL;
  98. mod.mod_op = LDAP_MOD_REPLACE | LDAP_MOD_BVALUES;
  99. mod.mod_type = cfg->state_attr_name;
  100. mod.mod_bvalues = vals;
  101. mods[0] = &mod;
  102. mods[1] = NULL;
  103. modpb = slapi_pblock_new();
  104. slapi_modify_internal_set_pb( modpb, dn, mods, NULL, NULL,
  105. plugin_id, SLAPI_OP_FLAG_NO_ACCESS_CHECK |
  106. SLAPI_OP_FLAG_BYPASS_REFERRALS );
  107. slapi_modify_internal_pb( modpb );
  108. slapi_pblock_get( modpb, SLAPI_PLUGIN_INTOP_RESULT, &ldrc );
  109. if (ldrc != LDAP_SUCCESS) {
  110. slapi_log_error( SLAPI_LOG_FATAL, POST_PLUGIN_NAME,
  111. "Recording %s=%s failed on \"%s\" err=%d\n", cfg->state_attr_name,
  112. timestr, dn, ldrc );
  113. rc = -1;
  114. goto done;
  115. } else {
  116. slapi_log_error( SLAPI_LOG_PLUGIN, POST_PLUGIN_NAME,
  117. "Recorded %s=%s on \"%s\"\n", cfg->state_attr_name, timestr, dn );
  118. }
  119. done:
  120. slapi_pblock_destroy( modpb );
  121. slapi_ch_free_string( &timestr );
  122. return( rc );
  123. }
  124. /*
  125. Handles bind preop callbacks
  126. */
  127. int
  128. acct_bind_preop( Slapi_PBlock *pb )
  129. {
  130. char *dn = NULL;
  131. Slapi_DN *sdn = NULL;
  132. Slapi_Entry *target_entry = NULL;
  133. int rc = 0; /* Optimistic default */
  134. int ldrc;
  135. acctPolicy *policy = NULL;
  136. void *plugin_id;
  137. slapi_log_error( SLAPI_LOG_PLUGIN, PRE_PLUGIN_NAME,
  138. "=> acct_bind_preop\n" );
  139. plugin_id = get_identity();
  140. /* This does not give a copy, so don't free it */
  141. if( slapi_pblock_get( pb, SLAPI_BIND_TARGET, &dn ) != 0 ) {
  142. slapi_log_error( SLAPI_LOG_FATAL, PRE_PLUGIN_NAME,
  143. "Error retrieving target DN\n" );
  144. rc = -1;
  145. goto done;
  146. }
  147. /* The plugin wouldn't get called for anonymous binds but let's check */
  148. if ( dn == NULL ) {
  149. goto done;
  150. }
  151. sdn = slapi_sdn_new_dn_byref( dn );
  152. ldrc = slapi_search_internal_get_entry( sdn, NULL, &target_entry,
  153. plugin_id );
  154. /* There was a problem retrieving the entry */
  155. if( ldrc != LDAP_SUCCESS ) {
  156. if( ldrc != LDAP_NO_SUCH_OBJECT ) {
  157. /* The problem is not a bad bind or virtual entry; halt bind */
  158. slapi_log_error( SLAPI_LOG_FATAL, PRE_PLUGIN_NAME,
  159. "Failed to retrieve entry \"%s\": %d\n", dn, ldrc );
  160. rc = -1;
  161. }
  162. goto done;
  163. }
  164. if( get_acctpolicy( pb, target_entry, plugin_id, &policy ) ) {
  165. slapi_log_error( SLAPI_LOG_FATAL, PRE_PLUGIN_NAME,
  166. "Account Policy object for \"%s\" is missing\n", dn );
  167. rc = -1;
  168. goto done;
  169. }
  170. /* Null policy means target isnt's under the influence of a policy */
  171. if( policy == NULL ) {
  172. slapi_log_error( SLAPI_LOG_PLUGIN, PRE_PLUGIN_NAME,
  173. "\"%s\" is not governed by an account policy\n", dn);
  174. goto done;
  175. }
  176. /* Check whether the account is in violation of inactivity limit */
  177. rc = acct_inact_limit( pb, dn, target_entry, policy );
  178. /* ...Any additional account policy enforcement goes here... */
  179. done:
  180. /* Internal error */
  181. if( rc == -1 ) {
  182. slapi_send_ldap_result( pb, LDAP_UNWILLING_TO_PERFORM, NULL, NULL, 0, NULL );
  183. }
  184. slapi_entry_free( target_entry );
  185. slapi_sdn_free( &sdn );
  186. free_acctpolicy( &policy );
  187. slapi_log_error( SLAPI_LOG_PLUGIN, PRE_PLUGIN_NAME,
  188. "<= acct_bind_preop\n" );
  189. return( rc == 0 ? CALLBACK_OK : CALLBACK_ERR );
  190. }
  191. /*
  192. This is called after binds, it updates an attribute in the entry that the
  193. bind DN corresponds to with the current time if it has an account policy
  194. specifier.
  195. */
  196. int
  197. acct_bind_postop( Slapi_PBlock *pb )
  198. {
  199. char *dn = NULL;
  200. int ldrc, tracklogin = 0;
  201. int rc = 0; /* Optimistic default */
  202. Slapi_DN *sdn = NULL;
  203. Slapi_Entry *target_entry = NULL;
  204. acctPluginCfg *cfg;
  205. void *plugin_id;
  206. slapi_log_error( SLAPI_LOG_PLUGIN, POST_PLUGIN_NAME,
  207. "=> acct_bind_postop\n" );
  208. plugin_id = get_identity();
  209. /* Retrieving SLAPI_CONN_DN from the pb gives a copy */
  210. if( slapi_pblock_get( pb, SLAPI_CONN_DN, &dn ) != 0 ) {
  211. slapi_log_error( SLAPI_LOG_FATAL, POST_PLUGIN_NAME,
  212. "Error retrieving bind DN\n" );
  213. rc = -1;
  214. goto done;
  215. }
  216. /* Client is anonymously bound */
  217. if( dn == NULL ) {
  218. goto done;
  219. }
  220. cfg = get_config();
  221. tracklogin = cfg->always_record_login;
  222. /* We're not always tracking logins, so check whether the entry is
  223. covered by an account policy to decide whether we should track */
  224. if( tracklogin == 0 ) {
  225. sdn = slapi_sdn_new_dn_byref( dn );
  226. ldrc = slapi_search_internal_get_entry( sdn, NULL, &target_entry,
  227. plugin_id );
  228. if( ldrc != LDAP_SUCCESS ) {
  229. slapi_log_error( SLAPI_LOG_FATAL, POST_PLUGIN_NAME,
  230. "Failed to retrieve entry \"%s\": %d\n", dn, ldrc );
  231. rc = -1;
  232. goto done;
  233. } else {
  234. if( target_entry && has_attr( target_entry,
  235. cfg->spec_attr_name, NULL ) ) {
  236. /* This account has a policy specifier */
  237. tracklogin = 1;
  238. }
  239. }
  240. }
  241. if( tracklogin ) {
  242. rc = acct_record_login( dn );
  243. }
  244. /* ...Any additional account policy postops go here... */
  245. done:
  246. if( rc == -1 ) {
  247. slapi_send_ldap_result( pb, LDAP_UNWILLING_TO_PERFORM, NULL, NULL, 0, NULL );
  248. }
  249. slapi_entry_free( target_entry );
  250. slapi_sdn_free( &sdn );
  251. slapi_ch_free_string( &dn );
  252. slapi_log_error( SLAPI_LOG_PLUGIN, POST_PLUGIN_NAME,
  253. "<= acct_bind_postop\n" );
  254. return( rc == 0 ? CALLBACK_OK : CALLBACK_ERR );
  255. }