+static const char *lmpats[] = {
+ "base",
+ "base",
+ "onelevel",
+ "subtree",
+ "children",
+ "regex",
+ "anonymous",
+ "users",
+ "*"
+};
+
+#define WHATSLEFT ( buflen - ( ptr - bv->bv_val ) )
+
+/* Caller must provide an adequately sized buffer in bv */
+int
+limits_unparse( struct slap_limits *lim, struct berval *bv, ber_len_t buflen )
+{
+ struct berval btmp;
+ char *ptr;
+ int lm;
+
+ if ( !bv || !bv->bv_val ) return -1;
+
+ ptr = bv->bv_val;
+
+ if (( lim->lm_flags & SLAP_LIMITS_TYPE_MASK ) == SLAP_LIMITS_TYPE_GROUP ) {
+ if ( WHATSLEFT <= STRLENOF( "group/" "/" "=\"" "\"" )
+ + lim->lm_group_oc->soc_cname.bv_len
+ + lim->lm_group_ad->ad_cname.bv_len
+ + lim->lm_pat.bv_len ) return -1;
+
+ ptr = lutil_strcopy( ptr, "group/" );
+ ptr = lutil_strcopy( ptr, lim->lm_group_oc->soc_cname.bv_val );
+ *ptr++ = '/';
+ ptr = lutil_strcopy( ptr, lim->lm_group_ad->ad_cname.bv_val );
+ ptr = lutil_strcopy( ptr, "=\"" );
+ ptr = lutil_strcopy( ptr, lim->lm_pat.bv_val );
+ *ptr++ = '"';
+ } else {
+ lm = lim->lm_flags & SLAP_LIMITS_MASK;
+ switch( lm ) {
+ case SLAP_LIMITS_ANONYMOUS:
+ case SLAP_LIMITS_USERS:
+ case SLAP_LIMITS_ANY:
+ if ( WHATSLEFT <= strlen( lmpats[lm] ) ) return -1;
+ ptr = lutil_strcopy( ptr, lmpats[lm] );
+ break;
+ case SLAP_LIMITS_UNDEFINED:
+ case SLAP_LIMITS_EXACT:
+ case SLAP_LIMITS_ONE:
+ case SLAP_LIMITS_SUBTREE:
+ case SLAP_LIMITS_CHILDREN:
+ case SLAP_LIMITS_REGEX:
+ if ( WHATSLEFT <= STRLENOF( "dn." "=" "\"" "\"" )
+ + strlen( lmpats[lm] ) + lim->lm_pat.bv_len ) return -1;
+ ptr = lutil_strcopy( ptr, "dn." );
+ ptr = lutil_strcopy( ptr, lmpats[lm] );
+ *ptr++ = '=';
+ *ptr++ = '"';
+ ptr = lutil_strcopy( ptr, lim->lm_pat.bv_val );
+ *ptr++ = '"';
+ break;
+ }
+ }
+ if ( WHATSLEFT <= STRLENOF( " " ) ) return -1;
+ *ptr++ = ' ';
+ bv->bv_len = ptr - bv->bv_val;
+ btmp.bv_val = ptr;
+ btmp.bv_len = 0;
+ if ( limits_unparse_one( &lim->lm_limits,
+ SLAP_LIMIT_SIZE|SLAP_LIMIT_TIME,
+ &btmp, WHATSLEFT ) )
+ {
+ return -1;
+ }
+ bv->bv_len += btmp.bv_len;
+ return 0;
+}
+
+/* Caller must provide an adequately sized buffer in bv */
+int
+limits_unparse_one( struct slap_limits_set *lim, int which, struct berval *bv, ber_len_t buflen )
+{
+ char *ptr;
+
+ if ( !bv || !bv->bv_val ) return;
+
+ ptr = bv->bv_val;
+
+ if ( which & SLAP_LIMIT_SIZE ) {
+ if ( lim->lms_s_soft != SLAPD_DEFAULT_SIZELIMIT ) {
+
+ /* If same as global limit, drop it */
+ if ( lim != &frontendDB->be_def_limit &&
+ lim->lms_s_soft == frontendDB->be_def_limit.lms_s_soft )
+ {
+ goto s_hard;
+ /* If there's also a hard limit, fully qualify this one */
+ } else if ( lim->lms_s_hard ) {
+ if ( WHATSLEFT <= STRLENOF( " size.soft=" ) ) return -1;
+ ptr = lutil_strcopy( ptr, " size.soft=" );
+
+ /* If doing both size & time, qualify this */
+ } else if ( which & SLAP_LIMIT_TIME ) {
+ if ( WHATSLEFT <= STRLENOF( " size=" ) ) return -1;
+ ptr = lutil_strcopy( ptr, " size=" );
+ }
+
+ if ( lim->lms_s_soft == -1 ) {
+ if ( WHATSLEFT <= STRLENOF( "unlimited" ) ) return -1;
+ ptr = lutil_strcopy( ptr, "unlimited" );
+ } else {
+ ptr += snprintf( ptr, WHATSLEFT, "%d", lim->lms_s_soft );
+ if ( WHATSLEFT < 0 ) return -1;
+ }
+ *ptr++ = ' ';
+ }
+s_hard:
+ if ( lim->lms_s_hard ) {
+ if ( WHATSLEFT <= STRLENOF( " size.hard=" ) ) return -1;
+ ptr = lutil_strcopy( ptr, " size.hard=" );
+ if ( lim->lms_s_hard == -1 ) {
+ if ( WHATSLEFT <= STRLENOF( "unlimited" ) ) return -1;
+ ptr = lutil_strcopy( ptr, "unlimited" );
+ } else {
+ ptr += snprintf( ptr, WHATSLEFT, "%d", lim->lms_s_hard );
+ if ( WHATSLEFT < 0 ) return -1;
+ }
+ *ptr++ = ' ';
+ }
+ if ( lim->lms_s_unchecked != -1 ) {
+ if ( WHATSLEFT <= STRLENOF( " size.unchecked=" ) ) return -1;
+ ptr = lutil_strcopy( ptr, " size.unchecked=" );
+ if ( lim->lms_s_unchecked == 0 ) {
+ if ( WHATSLEFT <= STRLENOF( "disabled" ) ) return -1;
+ ptr = lutil_strcopy( ptr, "disabled" );
+ } else {
+ ptr += snprintf( ptr, WHATSLEFT, "%d", lim->lms_s_unchecked );
+ if ( WHATSLEFT < 0 ) return -1;
+ }
+ *ptr++ = ' ';
+ }
+ if ( lim->lms_s_pr_hide ) {
+ if ( WHATSLEFT <= STRLENOF( " size.pr=noEstimate " ) ) return -1;
+ ptr = lutil_strcopy( ptr, " size.pr=noEstimate " );
+ }
+ if ( lim->lms_s_pr ) {
+ if ( WHATSLEFT <= STRLENOF( " size.pr=" ) ) return -1;
+ ptr = lutil_strcopy( ptr, " size.pr=" );
+ if ( lim->lms_s_pr == -1 ) {
+ if ( WHATSLEFT <= STRLENOF( "unlimited" ) ) return -1;
+ ptr = lutil_strcopy( ptr, "unlimited" );
+ } else {
+ ptr += snprintf( ptr, WHATSLEFT, "%d", lim->lms_s_pr );
+ if ( WHATSLEFT < 0 ) return -1;
+ }
+ *ptr++ = ' ';
+ }
+ if ( lim->lms_s_pr_total ) {
+ if ( WHATSLEFT <= STRLENOF( " size.prtotal=" ) ) return -1;
+ ptr = lutil_strcopy( ptr, " size.prtotal=" );
+ if ( lim->lms_s_pr_total == -1 ) {
+ if ( WHATSLEFT <= STRLENOF( "unlimited" ) ) return -1;
+ ptr = lutil_strcopy( ptr, "unlimited" );
+ } else if ( lim->lms_s_pr_total == -2 ) {
+ if ( WHATSLEFT <= STRLENOF( "disabled" ) ) return -1;
+ ptr = lutil_strcopy( ptr, "disabled" );
+ } else {
+ ptr += snprintf( ptr, WHATSLEFT, "%d", lim->lms_s_pr_total );
+ if ( WHATSLEFT < 0 ) return -1;
+ }
+ *ptr++ = ' ';
+ }
+ }
+
+ if ( which & SLAP_LIMIT_TIME ) {
+ if ( lim->lms_t_soft != SLAPD_DEFAULT_TIMELIMIT ) {
+
+ /* If same as global limit, drop it */
+ if ( lim != &frontendDB->be_def_limit &&
+ lim->lms_t_soft == frontendDB->be_def_limit.lms_t_soft )
+ {
+ goto t_hard;
+
+ /* If there's also a hard limit, fully qualify this one */
+ } else if ( lim->lms_t_hard ) {
+ if ( WHATSLEFT <= STRLENOF( " time.soft=" ) ) return -1;
+ ptr = lutil_strcopy( ptr, " time.soft=" );
+
+ /* If doing both size & time, qualify this */
+ } else if ( which & SLAP_LIMIT_SIZE ) {
+ if ( WHATSLEFT <= STRLENOF( " time=" ) ) return -1;
+ ptr = lutil_strcopy( ptr, " time=" );
+ }
+
+ if ( lim->lms_t_soft == -1 ) {
+ if ( WHATSLEFT <= STRLENOF( "unlimited" ) ) return -1;
+ ptr = lutil_strcopy( ptr, "unlimited" );
+ } else {
+ ptr += snprintf( ptr, WHATSLEFT, "%d", lim->lms_t_soft );
+ if ( WHATSLEFT < 0 ) return -1;
+ }
+ *ptr++ = ' ';
+ }
+t_hard:
+ if ( lim->lms_t_hard ) {
+ if ( WHATSLEFT <= STRLENOF( " time.hard=" ) ) return -1;
+ ptr = lutil_strcopy( ptr, " time.hard=" );
+ if ( lim->lms_t_hard == -1 ) {
+ if ( WHATSLEFT <= STRLENOF( "unlimited" ) ) return -1;
+ ptr = lutil_strcopy( ptr, "unlimited" );
+ } else {
+ ptr += snprintf( ptr, WHATSLEFT, "%d", lim->lms_t_hard );
+ if ( WHATSLEFT < 0 ) return -1;
+ }
+ *ptr++ = ' ';
+ }
+ }
+ if ( ptr != bv->bv_val ) {
+ ptr--;
+ *ptr = '\0';
+ bv->bv_len = ptr - bv->bv_val;
+ }
+}
+
+int
+limits_check( Operation *op, SlapReply *rs )
+{
+ assert( op != NULL );
+ assert( rs != NULL );
+ /* FIXME: should this be always true? */
+ assert( op->o_tag == LDAP_REQ_SEARCH);
+
+ /* protocol only allows 0..maxInt;
+ *
+ * internal searches:
+ * - may use SLAP_NO_LIMIT ( = -1 ) to indicate no limits;
+ * - should use slimit = N and tlimit = SLAP_NO_LIMIT to
+ * indicate searches that should return exactly N matches,
+ * and handle errors thru a callback (see for instance
+ * slap_sasl_match() and slap_sasl2dn())
+ */
+ if ( op->ors_tlimit == SLAP_NO_LIMIT && op->ors_slimit == SLAP_NO_LIMIT ) {
+ return 0;
+ }
+
+ /* allow root to set no limit */
+ if ( be_isroot( op ) ) {
+ op->ors_limit = NULL;
+
+ if ( op->ors_tlimit == 0 ) {
+ op->ors_tlimit = SLAP_NO_LIMIT;
+ }
+
+ if ( op->ors_slimit == 0 ) {
+ op->ors_slimit = SLAP_NO_LIMIT;
+ }
+
+ /* if not root, get appropriate limits */
+ } else {
+ ( void ) limits_get( op, &op->o_ndn, &op->ors_limit );
+
+ assert( op->ors_limit != NULL );
+
+ /* if no limit is required, use soft limit */
+ if ( op->ors_tlimit == 0 ) {
+ op->ors_tlimit = op->ors_limit->lms_t_soft;
+
+ /* limit required: check if legal */
+ } else {
+ if ( op->ors_limit->lms_t_hard == 0 ) {
+ if ( op->ors_limit->lms_t_soft > 0
+ && ( op->ors_tlimit > op->ors_limit->lms_t_soft ) ) {
+ op->ors_tlimit = op->ors_limit->lms_t_soft;
+ }
+
+ } else if ( op->ors_limit->lms_t_hard > 0 ) {
+#ifdef ABOVE_HARD_LIMIT_IS_ERROR
+ if ( op->ors_tlimit == SLAP_MAX_LIMIT ) {
+ op->ors_tlimit = op->ors_limit->lms_t_hard;
+
+ } else if ( op->ors_tlimit > op->ors_limit->lms_t_hard ) {
+ /* error if exceeding hard limit */
+ rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
+ send_ldap_result( op, rs );
+ rs->sr_err = LDAP_SUCCESS;
+ return -1;
+ }
+#else /* ! ABOVE_HARD_LIMIT_IS_ERROR */
+ if ( op->ors_tlimit > op->ors_limit->lms_t_hard ) {
+ op->ors_tlimit = op->ors_limit->lms_t_hard;
+ }
+#endif /* ! ABOVE_HARD_LIMIT_IS_ERROR */
+ }
+ }
+
+ /* else leave as is */
+
+ /* don't even get to backend if candidate check is disabled */
+ if ( op->ors_limit->lms_s_unchecked == 0 ) {
+ rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
+ send_ldap_result( op, rs );
+ rs->sr_err = LDAP_SUCCESS;
+ return -1;
+ }
+
+ /* if paged results is requested */
+ if ( get_pagedresults( op ) > SLAP_CONTROL_IGNORED ) {
+ int slimit = -2;
+ int pr_total;
+ PagedResultsState *ps = op->o_pagedresults_state;
+
+ /* paged results is not allowed */
+ if ( op->ors_limit->lms_s_pr_total == -2 ) {
+ rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
+ rs->sr_text = "pagedResults control not allowed";
+ send_ldap_result( op, rs );
+ rs->sr_err = LDAP_SUCCESS;
+ rs->sr_text = NULL;
+ return -1;
+ }
+
+ if ( op->ors_limit->lms_s_pr > 0 && ps->ps_size > op->ors_limit->lms_s_pr ) {
+ rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
+ rs->sr_text = "illegal pagedResults page size";
+ send_ldap_result( op, rs );
+ rs->sr_err = LDAP_SUCCESS;
+ rs->sr_text = NULL;
+ return -1;
+ }
+
+ if ( op->ors_limit->lms_s_pr_total == 0 ) {
+ if ( op->ors_limit->lms_s_hard == 0 ) {
+ pr_total = op->ors_limit->lms_s_soft;
+ } else {
+ pr_total = op->ors_limit->lms_s_hard;
+ }
+ } else {
+ pr_total = op->ors_limit->lms_s_pr_total;
+ }
+
+ if ( pr_total == -1 ) {
+ if ( op->ors_slimit == 0 || op->ors_slimit == SLAP_MAX_LIMIT ) {
+ slimit = -1;
+
+ } else {
+ slimit = op->ors_slimit - ps->ps_count;
+ }
+
+#ifdef ABOVE_HARD_LIMIT_IS_ERROR
+ } else if ( pr_total > 0 && op->ors_slimit != SLAP_MAX_LIMIT
+ && ( op->ors_slimit == SLAP_NO_LIMIT || op->ors_slimit > pr_total ) )
+ {
+ rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
+ send_ldap_result( op, rs );
+ rs->sr_err = LDAP_SUCCESS;
+ return -1;
+#endif /* ! ABOVE_HARD_LIMIT_IS_ERROR */
+
+ } else {
+ /* if no limit is required, use soft limit */
+ int total;
+ int slimit2;
+
+ /* first round of pagedResults: set count to any appropriate limit */
+
+ /* if the limit is set, check that it does not violate any server-side limit */
+#ifdef ABOVE_HARD_LIMIT_IS_ERROR
+ if ( op->ors_slimit == SLAP_MAX_LIMIT ) {
+ slimit2 = op->ors_slimit = pr_total;
+#else /* ! ABOVE_HARD_LIMIT_IS_ERROR */
+ if ( op->ors_slimit == SLAP_MAX_LIMIT || op->ors_slimit > pr_total ) {
+ slimit2 = op->ors_slimit = pr_total;
+#endif /* ! ABOVE_HARD_LIMIT_IS_ERROR */
+
+ } else if ( op->ors_slimit == 0 ) {
+ slimit2 = pr_total;
+
+ } else {
+ slimit2 = op->ors_slimit;
+ }
+
+ total = slimit2 - ps->ps_count;
+
+ if ( total >= 0 ) {
+ if ( op->ors_limit->lms_s_pr > 0 ) {
+ /* use the smallest limit set by total/per page */
+ if ( total < op->ors_limit->lms_s_pr ) {
+ slimit = total;
+
+ } else {
+ /* use the perpage limit if any
+ * NOTE: + 1 because the given value must be legal */
+ slimit = op->ors_limit->lms_s_pr + 1;
+ }
+
+ } else {
+ /* use the total limit if any */
+ slimit = total;
+ }
+
+ } else if ( op->ors_limit->lms_s_pr > 0 ) {
+ /* use the perpage limit if any
+ * NOTE: + 1 because the given value must be legal */
+ slimit = op->ors_limit->lms_s_pr + 1;
+
+ } else {
+ /* use the standard hard/soft limit if any */
+ slimit = op->ors_limit->lms_s_hard;
+ }
+ }
+
+ /* if got any limit, use it */
+ if ( slimit != -2 ) {
+ if ( op->ors_slimit == 0 ) {
+ op->ors_slimit = slimit;
+
+ } else if ( slimit > 0 ) {
+ if ( op->ors_slimit - ps->ps_count > slimit ) {
+ rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
+ send_ldap_result( op, rs );
+ rs->sr_err = LDAP_SUCCESS;
+ return -1;
+ }
+ op->ors_slimit = slimit;
+ }
+
+ } else {
+ /* use the standard hard/soft limit if any */
+ op->ors_slimit = pr_total;
+ }
+
+ /* no limit requested: use soft, whatever it is */
+ } else if ( op->ors_slimit == 0 ) {
+ op->ors_slimit = op->ors_limit->lms_s_soft;
+
+ /* limit requested: check if legal */
+ } else {
+ /* hard limit as soft (traditional behavior) */
+ if ( op->ors_limit->lms_s_hard == 0 ) {
+ if ( op->ors_limit->lms_s_soft > 0
+ && op->ors_slimit > op->ors_limit->lms_s_soft ) {
+ op->ors_slimit = op->ors_limit->lms_s_soft;
+ }
+
+ /* explicit hard limit: error if violated */
+ } else if ( op->ors_limit->lms_s_hard > 0 ) {
+#ifdef ABOVE_HARD_LIMIT_IS_ERROR
+ if ( op->ors_slimit == SLAP_MAX_LIMIT ) {
+ op->ors_slimit = op->ors_limit->lms_s_hard;
+
+ } else if ( op->ors_slimit > op->ors_limit->lms_s_hard ) {
+ /* if limit exceeds hard, error */
+ rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
+ send_ldap_result( op, rs );
+ rs->sr_err = LDAP_SUCCESS;
+ return -1;
+ }
+#else /* ! ABOVE_HARD_LIMIT_IS_ERROR */
+ if ( op->ors_slimit > op->ors_limit->lms_s_hard ) {
+ op->ors_slimit = op->ors_limit->lms_s_hard;
+ }
+#endif /* ! ABOVE_HARD_LIMIT_IS_ERROR */
+ }
+ }
+
+ /* else leave as is */
+ }
+
+ return 0;
+}
+
+void
+limits_destroy(
+ struct slap_limits **lm )
+{
+ int i;
+
+ if ( lm == NULL ) {
+ return;
+ }
+
+ for ( i = 0; lm[ i ]; i++ ) {
+ switch ( lm[ i ]->lm_flags & SLAP_LIMITS_MASK ) {
+ case SLAP_LIMITS_REGEX:
+ regfree( &lm[ i ]->lm_regex );
+ break;
+
+ case SLAP_LIMITS_EXACT:
+ case SLAP_LIMITS_ONE:
+ case SLAP_LIMITS_SUBTREE:
+ case SLAP_LIMITS_CHILDREN:
+ if ( !BER_BVISNULL( &lm[ i ]->lm_pat ) ) {
+ ch_free( lm[ i ]->lm_pat.bv_val );
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ ch_free( lm[ i ] );
+ }
+
+ ch_free( lm );
+}