acct_plugin.c 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  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, const 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( cfg->alt_state_attr_name && (( 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. /* the primary or alternate attribute might not yet exist eg.
  45. * if only lastlogintime is specified and it id the first login
  46. */
  47. slapi_log_error( SLAPI_LOG_PLUGIN, PRE_PLUGIN_NAME,
  48. "\"%s\" has no value for stateattr or altstateattr \n", dn );
  49. goto done;
  50. }
  51. last_t = gentimeToEpochtime( lasttimestr );
  52. cur_t = time( (time_t*)0 );
  53. lim_t = policy->inactivitylimit;
  54. /* Finally do the time comparison */
  55. if( cur_t > last_t + lim_t ) {
  56. slapi_log_error( SLAPI_LOG_PLUGIN, PRE_PLUGIN_NAME,
  57. "\"%s\" has exceeded inactivity limit (%ld > (%ld + %ld))\n",
  58. dn, cur_t, last_t, lim_t );
  59. rc = 1;
  60. goto done;
  61. } else {
  62. slapi_log_error( SLAPI_LOG_PLUGIN, PRE_PLUGIN_NAME,
  63. "\"%s\" is within inactivity limit (%ld < (%ld + %ld))\n",
  64. dn, cur_t, last_t, lim_t );
  65. }
  66. done:
  67. /* Deny bind; the account has exceeded the inactivity limit */
  68. if( rc == 1 ) {
  69. slapi_send_ldap_result( pb, LDAP_CONSTRAINT_VIOLATION, NULL,
  70. "Account inactivity limit exceeded."
  71. " Contact system administrator to reset.", 0, NULL );
  72. }
  73. slapi_ch_free_string( &lasttimestr );
  74. return( rc );
  75. }
  76. /*
  77. This is called after binds, it updates an attribute in the account
  78. with the current time.
  79. */
  80. static int
  81. acct_record_login( const char *dn )
  82. {
  83. int ldrc;
  84. int rc = 0; /* Optimistic default */
  85. LDAPMod *mods[2];
  86. LDAPMod mod;
  87. struct berval *vals[2];
  88. struct berval val;
  89. char *timestr = NULL;
  90. acctPluginCfg *cfg;
  91. void *plugin_id;
  92. Slapi_PBlock *modpb = NULL;
  93. int skip_mod_attrs = 1; /* value doesn't matter as long as not NULL */
  94. cfg = get_config();
  95. /* if we are not allowed to modify the state attr we're done
  96. * this could be intentional, so just return
  97. */
  98. if (! update_is_allowed_attr(cfg->state_attr_name) )
  99. return rc;
  100. plugin_id = get_identity();
  101. timestr = epochtimeToGentime( time( (time_t*)0 ) );
  102. val.bv_val = timestr;
  103. val.bv_len = strlen( val.bv_val );
  104. vals [0] = &val;
  105. vals [1] = NULL;
  106. mod.mod_op = LDAP_MOD_REPLACE | LDAP_MOD_BVALUES;
  107. mod.mod_type = cfg->state_attr_name;
  108. mod.mod_bvalues = vals;
  109. mods[0] = &mod;
  110. mods[1] = NULL;
  111. modpb = slapi_pblock_new();
  112. slapi_modify_internal_set_pb( modpb, dn, mods, NULL, NULL,
  113. plugin_id, SLAPI_OP_FLAG_NO_ACCESS_CHECK |
  114. SLAPI_OP_FLAG_BYPASS_REFERRALS );
  115. slapi_pblock_set( modpb, SLAPI_SKIP_MODIFIED_ATTRS, &skip_mod_attrs );
  116. slapi_modify_internal_pb( modpb );
  117. slapi_pblock_get( modpb, SLAPI_PLUGIN_INTOP_RESULT, &ldrc );
  118. if (ldrc != LDAP_SUCCESS) {
  119. slapi_log_error( SLAPI_LOG_FATAL, POST_PLUGIN_NAME,
  120. "Recording %s=%s failed on \"%s\" err=%d\n", cfg->state_attr_name,
  121. timestr, dn, ldrc );
  122. rc = -1;
  123. goto done;
  124. } else {
  125. slapi_log_error( SLAPI_LOG_PLUGIN, POST_PLUGIN_NAME,
  126. "Recorded %s=%s on \"%s\"\n", cfg->state_attr_name, timestr, dn );
  127. }
  128. done:
  129. slapi_pblock_destroy( modpb );
  130. slapi_ch_free_string( &timestr );
  131. return( rc );
  132. }
  133. /*
  134. Handles bind preop callbacks
  135. */
  136. int
  137. acct_bind_preop( Slapi_PBlock *pb )
  138. {
  139. const char *dn = NULL;
  140. Slapi_DN *sdn = NULL;
  141. Slapi_Entry *target_entry = NULL;
  142. int rc = 0; /* Optimistic default */
  143. int ldrc;
  144. acctPolicy *policy = NULL;
  145. void *plugin_id;
  146. slapi_log_error( SLAPI_LOG_PLUGIN, PRE_PLUGIN_NAME,
  147. "=> acct_bind_preop\n" );
  148. plugin_id = get_identity();
  149. /* This does not give a copy, so don't free it */
  150. if( slapi_pblock_get( pb, SLAPI_BIND_TARGET_SDN, &sdn ) != 0 ) {
  151. slapi_log_error( SLAPI_LOG_FATAL, PRE_PLUGIN_NAME,
  152. "Error retrieving target DN\n" );
  153. rc = -1;
  154. goto done;
  155. }
  156. dn = slapi_sdn_get_dn(sdn);
  157. /* The plugin wouldn't get called for anonymous binds but let's check */
  158. if ( dn == NULL ) {
  159. goto done;
  160. }
  161. ldrc = slapi_search_internal_get_entry( sdn, NULL, &target_entry,
  162. plugin_id );
  163. /* There was a problem retrieving the entry */
  164. if( ldrc != LDAP_SUCCESS ) {
  165. if( ldrc != LDAP_NO_SUCH_OBJECT ) {
  166. /* The problem is not a bad bind or virtual entry; halt bind */
  167. slapi_log_error( SLAPI_LOG_FATAL, PRE_PLUGIN_NAME,
  168. "Failed to retrieve entry \"%s\": %d\n", dn, ldrc );
  169. rc = -1;
  170. }
  171. goto done;
  172. }
  173. if( get_acctpolicy( pb, target_entry, plugin_id, &policy ) ) {
  174. slapi_log_error( SLAPI_LOG_FATAL, PRE_PLUGIN_NAME,
  175. "Account Policy object for \"%s\" is missing\n", dn );
  176. rc = -1;
  177. goto done;
  178. }
  179. /* Null policy means target isnt's under the influence of a policy */
  180. if( policy == NULL ) {
  181. slapi_log_error( SLAPI_LOG_PLUGIN, PRE_PLUGIN_NAME,
  182. "\"%s\" is not governed by an account policy\n", dn);
  183. goto done;
  184. }
  185. /* Check whether the account is in violation of inactivity limit */
  186. rc = acct_inact_limit( pb, dn, target_entry, policy );
  187. /* ...Any additional account policy enforcement goes here... */
  188. done:
  189. /* Internal error */
  190. if( rc == -1 ) {
  191. slapi_send_ldap_result( pb, LDAP_UNWILLING_TO_PERFORM, NULL, NULL, 0, NULL );
  192. }
  193. slapi_entry_free( target_entry );
  194. free_acctpolicy( &policy );
  195. slapi_log_error( SLAPI_LOG_PLUGIN, PRE_PLUGIN_NAME,
  196. "<= acct_bind_preop\n" );
  197. return( rc == 0 ? CALLBACK_OK : CALLBACK_ERR );
  198. }
  199. /*
  200. This is called after binds, it updates an attribute in the entry that the
  201. bind DN corresponds to with the current time if it has an account policy
  202. specifier.
  203. */
  204. int
  205. acct_bind_postop( Slapi_PBlock *pb )
  206. {
  207. char *dn = NULL;
  208. int ldrc, tracklogin = 0;
  209. int rc = 0; /* Optimistic default */
  210. Slapi_DN *sdn = NULL;
  211. Slapi_Entry *target_entry = NULL;
  212. acctPluginCfg *cfg;
  213. void *plugin_id;
  214. slapi_log_error( SLAPI_LOG_PLUGIN, POST_PLUGIN_NAME,
  215. "=> acct_bind_postop\n" );
  216. plugin_id = get_identity();
  217. /* Retrieving SLAPI_CONN_DN from the pb gives a copy */
  218. if( slapi_pblock_get( pb, SLAPI_CONN_DN, &dn ) != 0 ) {
  219. slapi_log_error( SLAPI_LOG_FATAL, POST_PLUGIN_NAME,
  220. "Error retrieving bind DN\n" );
  221. rc = -1;
  222. goto done;
  223. }
  224. /* Client is anonymously bound */
  225. if( dn == NULL ) {
  226. goto done;
  227. }
  228. cfg = get_config();
  229. tracklogin = cfg->always_record_login;
  230. /* We're not always tracking logins, so check whether the entry is
  231. covered by an account policy to decide whether we should track */
  232. if( tracklogin == 0 ) {
  233. sdn = slapi_sdn_new_normdn_byref( dn );
  234. ldrc = slapi_search_internal_get_entry( sdn, NULL, &target_entry,
  235. plugin_id );
  236. if( ldrc != LDAP_SUCCESS ) {
  237. slapi_log_error( SLAPI_LOG_FATAL, POST_PLUGIN_NAME,
  238. "Failed to retrieve entry \"%s\": %d\n", dn, ldrc );
  239. rc = -1;
  240. goto done;
  241. } else {
  242. if( target_entry && has_attr( target_entry,
  243. cfg->spec_attr_name, NULL ) ) {
  244. tracklogin = 1;
  245. }
  246. }
  247. }
  248. if( tracklogin ) {
  249. rc = acct_record_login( dn );
  250. }
  251. /* ...Any additional account policy postops go here... */
  252. done:
  253. if( rc == -1 ) {
  254. slapi_send_ldap_result( pb, LDAP_UNWILLING_TO_PERFORM, NULL, NULL, 0, NULL );
  255. }
  256. slapi_entry_free( target_entry );
  257. slapi_sdn_free( &sdn );
  258. slapi_ch_free_string( &dn );
  259. slapi_log_error( SLAPI_LOG_PLUGIN, POST_PLUGIN_NAME,
  260. "<= acct_bind_postop\n" );
  261. return( rc == 0 ? CALLBACK_OK : CALLBACK_ERR );
  262. }