Commit 339b9c37 authored by Pierangelo Masarati's avatar Pierangelo Masarati
Browse files

rfc2589 support (ITS#4293)

parent a17df0e8
......@@ -14,9 +14,11 @@
## <http://www.OpenLDAP.org/license.html>.
SRCS = ldapsearch.c ldapmodify.c ldapdelete.c ldapmodrdn.c \
ldappasswd.c ldapwhoami.c ldapcompare.c common.c
ldappasswd.c ldapwhoami.c ldapcompare.c \
ldapexop.c common.c
OBJS = ldapsearch.o ldapmodify.o ldapdelete.o ldapmodrdn.o \
ldappasswd.o ldapwhoami.o ldapcompare.o common.o
ldappasswd.o ldapwhoami.o ldapcompare.o \
ldapexop.o common.o
LDAP_INCDIR= ../../include
LDAP_LIBDIR= ../../libraries
......@@ -27,10 +29,10 @@ XLIBS = $(LDAP_L)
XXLIBS = $(SECURITY_LIBS) $(LUTIL_LIBS)
XSRCS = ldsversion.c ldmversion.c lddversion.c ldrversion.c \
ldpversion.c ldwversion.c ldcversion.c
ldpversion.c ldwversion.c ldcversion.c ldeversion.c
PROGRAMS = ldapsearch ldapmodify ldapdelete ldapmodrdn \
ldappasswd ldapwhoami ldapcompare
ldappasswd ldapwhoami ldapcompare ldapexop
ldapsearch: ldsversion.o
......@@ -54,6 +56,9 @@ ldapwhoami: ldwversion.o
ldapcompare: ldcversion.o
$(LTLINK) -o $@ ldapcompare.o common.o ldcversion.o $(LIBS)
ldapexop: ldeversion.o
$(LTLINK) -o $@ ldapexop.o common.o ldeversion.o $(LIBS)
ldsversion.c: Makefile
@-$(RM) $@
$(MKVERSION) $(MKVOPTS) ldapsearch > $@
......@@ -96,6 +101,12 @@ ldcversion.c: Makefile
ldcversion.o: ldapcompare.o common.o $(XLIBS)
ldeversion.c: Makefile
@-$(RM) $@
$(MKVERSION) $(MKVOPTS) ldapexop > $@
ldeversion.o: ldapexop.o common.o $(XLIBS)
install-local: FORCE
-$(MKDIR) $(DESTDIR)$(bindir)
@( \
......
/* ldapexop.c -- a tool for performing well-known extended operations */
/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
*
* Copyright 2005 The OpenLDAP Foundation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted only as authorized by the OpenLDAP
* Public License.
*
* A copy of this license is available in the file LICENSE in the
* top-level directory of the distribution or, alternatively, at
* <http://www.OpenLDAP.org/license.html>.
*/
/* ACKNOWLEDGEMENTS:
* This work was originally developed by Pierangelo Masarati for inclusion
* in OpenLDAP Software based, in part, on other client tools.
*/
#include "portable.h"
#include <stdio.h>
#include <ac/stdlib.h>
#include <ac/ctype.h>
#include <ac/socket.h>
#include <ac/string.h>
#include <ac/time.h>
#include <ac/unistd.h>
#include <ldap.h>
#include "lutil.h"
#include "lutil_ldap.h"
#include "ldap_defaults.h"
#include "common.h"
void
usage( void )
{
fprintf( stderr, _("Issue LDAP extended operations\n\n"));
fprintf( stderr, _("usage: %s [options]\n"), prog);
tool_common_usage();
exit( EXIT_FAILURE );
}
const char options[] = ""
"d:D:e:h:H:InO:p:QR:U:vVw:WxX:y:Y:Z";
int
handle_private_option( int i )
{
switch ( i ) {
default:
return 0;
}
return 1;
}
int
main( int argc, char *argv[] )
{
int rc;
char *user = NULL;
LDAP *ld = NULL;
char *matcheddn = NULL, *text = NULL, **refs = NULL;
int id, code;
LDAPMessage *res;
tool_init();
prog = lutil_progname( "ldapexop", argc, argv );
/* LDAPv3 only */
protocol = LDAP_VERSION3;
tool_args( argc, argv );
if ( argc - optind < 1 ) {
usage();
}
if ( pw_file || want_bindpw ) {
if ( pw_file ) {
rc = lutil_get_filed_password( pw_file, &passwd );
if( rc ) return EXIT_FAILURE;
} else {
passwd.bv_val = getpassphrase( _("Enter LDAP Password: ") );
passwd.bv_len = passwd.bv_val ? strlen( passwd.bv_val ) : 0;
}
}
ld = tool_conn_setup( 0, 0 );
tool_bind( ld );
argv += optind;
argc -= optind;
if ( strcasecmp( argv[ 0 ], "whoami" ) == 0 ) {
switch ( argc ) {
case 2:
user = argv[ 1 ];
case 1:
break;
default:
fprintf( stderr, "need [user]\n\n" );
usage();
}
if ( assertion || manageDSAit || noop ) {
fprintf( stderr, _("controls incompatible with WhoAmI exop\n\n") );
usage();
}
if ( authzid ) {
tool_server_controls( ld, NULL, 0 );
}
rc = ldap_whoami( ld, NULL, NULL, &id );
if ( rc != LDAP_SUCCESS ) {
tool_perror( "ldap_extended_operation", rc, NULL, NULL, NULL, NULL );
rc = EXIT_FAILURE;
goto skip;
}
} else if ( strcasecmp( argv[ 0 ], "cancel" ) == 0 ) {
int cancelid;
switch ( argc ) {
case 2:
if ( lutil_atoi( &cancelid, argv[ 1 ] ) != 0 || cancelid < 0 ) {
fprintf( stderr, "invalid cancelid=%s\n\n", argv[ 1 ] );
usage();
}
break;
default:
fprintf( stderr, "need cancelid\n\n" );
usage();
}
rc = ldap_cancel( ld, cancelid, NULL, NULL, &id );
if ( rc != LDAP_SUCCESS ) {
tool_perror( "ldap_cancel", rc, NULL, NULL, NULL, NULL );
rc = EXIT_FAILURE;
goto skip;
}
} else if ( strcasecmp( argv[ 0 ], "passwd" ) == 0 ) {
/* do we need this? */
} else if ( strcasecmp( argv[ 0 ], "refresh" ) == 0 ) {
int ttl = 3600;
struct berval dn;
switch ( argc ) {
case 3:
ttl = atoi( argv[ 2 ] );
case 2:
dn.bv_val = argv[ 1 ];
dn.bv_len = strlen( dn.bv_val );
case 1:
break;
default:
fprintf( stderr, _("need DN [ttl]\n\n") );
usage();
}
if ( assertion || manageDSAit || noop || authzid ) {
tool_server_controls( ld, NULL, 0 );
}
rc = ldap_refresh( ld, &dn, ttl, NULL, NULL, &id );
if ( rc != LDAP_SUCCESS ) {
tool_perror( "ldap_extended_operation", rc, NULL, NULL, NULL, NULL );
rc = EXIT_FAILURE;
goto skip;
}
} else if ( tool_is_oid( argv[ 0 ] ) ) {
} else {
fprintf( stderr, "unknown exop \"%s\"\n\n", argv[ 0 ] );
usage();
}
for ( ; ; ) {
struct timeval tv;
if ( tool_check_abandon( ld, id ) ) {
return LDAP_CANCELLED;
}
tv.tv_sec = 0;
tv.tv_usec = 100000;
rc = ldap_result( ld, LDAP_RES_ANY, LDAP_MSG_ALL, &tv, &res );
if ( rc < 0 ) {
tool_perror( "ldap_result", rc, NULL, NULL, NULL, NULL );
rc = EXIT_FAILURE;
goto skip;
}
if ( rc != 0 ) {
break;
}
}
rc = ldap_parse_result( ld, res,
&code, &matcheddn, &text, &refs, NULL, 0 );
if ( rc == LDAP_SUCCESS ) {
rc = code;
}
if ( rc != LDAP_SUCCESS ) {
tool_perror( "ldap_parse_result", rc, NULL, matcheddn, text, refs );
rc = EXIT_FAILURE;
goto skip;
}
if ( strcasecmp( argv[ 0 ], "whoami" ) == 0 ) {
char *retoid = NULL;
struct berval *retdata = NULL;
rc = ldap_parse_extended_result( ld, res, &retoid, &retdata, 1 );
if ( rc != LDAP_SUCCESS ) {
tool_perror( "ldap_parse_extended_result", rc, NULL, NULL, NULL, NULL );
rc = EXIT_FAILURE;
goto skip;
}
if ( retdata != NULL ) {
if ( retdata->bv_len == 0 ) {
printf(_("anonymous\n") );
} else {
printf("%s\n", retdata->bv_val );
}
}
ber_memfree( retoid );
ber_bvfree( retdata );
} else if ( strcasecmp( argv[ 0 ], "cancel" ) == 0 ) {
/* no extended response; returns specific errors */
assert( 0 );
} else if ( strcasecmp( argv[ 0 ], "passwd" ) == 0 ) {
} else if ( strcasecmp( argv[ 0 ], "refresh" ) == 0 ) {
int newttl;
rc = ldap_parse_refresh( ld, res, &newttl );
if ( rc != LDAP_SUCCESS ) {
tool_perror( "ldap_parse_refresh", rc, NULL, NULL, NULL, NULL );
rc = EXIT_FAILURE;
goto skip;
}
printf( "newttl=%d\n", newttl );
} else if ( tool_is_oid( argv[ 0 ] ) ) {
/* ... */
}
if( verbose || ( code != LDAP_SUCCESS ) || matcheddn || text || refs ) {
printf( _("Result: %s (%d)\n"), ldap_err2string( code ), code );
if( text && *text ) {
printf( _("Additional info: %s\n"), text );
}
if( matcheddn && *matcheddn ) {
printf( _("Matched DN: %s\n"), matcheddn );
}
if( refs ) {
int i;
for( i=0; refs[i]; i++ ) {
printf(_("Referral: %s\n"), refs[i] );
}
}
}
ber_memfree( text );
ber_memfree( matcheddn );
ber_memvfree( (void **) refs );
skip:
/* disconnect from server */
tool_unbind( ld );
tool_destroy();
return code == LDAP_SUCCESS ? EXIT_SUCCESS : EXIT_FAILURE;
}
......@@ -339,6 +339,7 @@ OL_ARG_ENABLE(sql,[ --enable-sql enable sql backend],
dnl ----------------------------------------------------------------
dnl SLAPD Overlay Options
Overlays="accesslog \
dds \
denyop \
dyngroup \
dynlist \
......@@ -360,6 +361,8 @@ OL_ARG_ENABLE(overlays,[ --enable-overlays enable all available overlays],
--, [no yes mod])dnl
OL_ARG_ENABLE(accesslog,[ --enable-accesslog In-Directory Access Logging overlay],
no, [no yes mod], ol_enable_overlays)
OL_ARG_ENABLE(dds,[ --enable-dds Dynamic Directory Services overlay],
no, [no yes mod], ol_enable_overlays)
OL_ARG_ENABLE(denyop,[ --enable-denyop Deny Operation overlay],
no, [no yes mod], ol_enable_overlays)
OL_ARG_ENABLE(dyngroup,[ --enable-dyngroup Dynamic Group overlay],
......@@ -614,6 +617,7 @@ BUILD_SHELL=no
BUILD_SQL=no
BUILD_ACCESSLOG=no
BUILD_DDS=no
BUILD_DENYOP=no
BUILD_DYNGROUP=no
BUILD_DYNLIST=no
......@@ -2913,6 +2917,18 @@ if test "$ol_enable_accesslog" != no ; then
AC_DEFINE_UNQUOTED(SLAPD_OVER_ACCESSLOG,$MFLAG,[define for In-Directory Access Logging overlay])
fi
if test "$ol_enable_dds" != no ; then
BUILD_DDS=$ol_enable_dds
if test "$ol_enable_dds" = mod ; then
MFLAG=SLAPD_MOD_DYNAMIC
SLAPD_DYNAMIC_OVERLAYS="$SLAPD_DYNAMIC_OVERLAYS dds.la"
else
MFLAG=SLAPD_MOD_STATIC
SLAPD_STATIC_OVERLAYS="$SLAPD_STATIC_OVERLAYS dds.o"
fi
AC_DEFINE_UNQUOTED(SLAPD_OVER_DDS,$MFLAG,[define for Dynamic Directory Services overlay])
fi
if test "$ol_enable_denyop" != no ; then
BUILD_DENYOP=$ol_enable_denyop
if test "$ol_enable_denyop" = mod ; then
......@@ -3130,6 +3146,7 @@ dnl backends
AC_SUBST(BUILD_SQL)
dnl overlays
AC_SUBST(BUILD_ACCESSLOG)
AC_SUBST(BUILD_DDS)
AC_SUBST(BUILD_DENYOP)
AC_SUBST(BUILD_DYNGROUP)
AC_SUBST(BUILD_DYNLIST)
......
.TH SLAPO-DDS 5 "RELEASEDATE" "OpenLDAP LDVERSION"
.\" Copyright 2005-2006 The OpenLDAP Foundation, All Rights Reserved.
.\" Copying restrictions apply. See the COPYRIGHT file.
.\" $OpenLDAP$
.SH NAME
slapo-dds \- dds overlay
.SH SYNOPSIS
ETCDIR/slapd.conf
.SH DESCRIPTION
The
.B dds
overlay to
.BR slapd (8)
implements dynamic objects as per RFC 2589.
The name
.B dds
stands for
Dynamic Dyrectory Services.
It allows to define dynamic objects, characterized by the
.B dynamicObject
objectClass.
Dynamic objects have a limited life, determined by a time-to-live (TTL)
that can be refreshed by means of a specific
.B refresh
extended operation.
This operation allows to set the Client Refresh Period (CRP),
namely the period between refreshes that is required to preserve the
dynamic object from expiration.
The expiration time is computed by adding the requested TTL to the
current time.
When dynamic objects reach the end of their life without being
further refreshed, they are automatically deleted; there is no guarantee
of immediate deletion, but clients should not count over it.
Dynamic objects can have subordinates, provided they also are dynamic
objects.
RFC 2589 does not specify what should the behavior of a dynamic
directory service be when a dynamic object with (dynamic) subordinates
expires.
In this implementation, the life of dynamic objects with subordinates
is prolonged until all the dynamic subordinates expired.
This
.BR slapd.conf (5)
directive adds the
.B dds
overlay to the current database:
.TP
.B overlay dds
.LP
The
.B dds
overlay may be used with any backend that implements the
.BR add ,
.BR modify ,
.BR search ,
and
.BR delete
operations.
Since its use may result in many internal entry lookups, adds
and deletes, it should be best used in conjunction with backends
that have resonably good write performances.
.LP
The config directives that are specific to the
.B dds
overlay are prefixed by
.BR dds\- ,
to avoid potential conflicts with directives specific to the underlying
database or to other stacked overlays.
.TP
.B dds\-max\-ttl <ttl>
Specifies the max TTL value; this is the default TTL newly created
dynamic objects receive, unless
.B dds\-default\-ttl
is set.
When the client with a refresh exop requests a TTL higher than it,
sizeLimitExceeded is returned.
This value must be between 86400 (1 day, the default) and 31557600
(1 year plus 6 hours, as per RFC 2589).
.TP
.B dds\-min\-ttl <ttl>
Specifies the min TTL value; clients requesting a lower TTL by means
of the refresh exop actually obtain this value as CRP.
If set to 0 (the default), no lower limit is set.
.TP
.B dds\-default\-ttl <ttl>
Specifies the default TTL value that newly created dynamic objects get.
If set to 0 (the default), the
.B dds\-max\-ttl
is used.
.TP
.B dds\-interval <ttl>
Specifies the interval between expiration checks; efaults to 1 hour.
.TP
.B dds\-tolerance <ttl>
Specifies an extra time that is added to the timer that actually wakes up
the thread that will delete an expired dynamic object.
So the nominal life of the entry is that specified in the
.B entryTtl
attribute, but its life will actually be
.BR " entryTtl + tolerance " .
Note that there is no guarantee that the life of a dynamic object will be
.I exactly
the requested TTL; due to implementation details, it may be longer, which
is allowed by RFC 2589.
By default, tolerance is 0.
.TP
.B dds\-max\-dynamicObjects <num>
Specifies the maximum number of dynamic objects that can simultaneously exist
within a naming context.
This allows to limit the amount of resources (mostly in terms of runqueue size)
that are used by dynamic objects.
By default, no limit is set.
.TP
.B dds-state {TRUE|false}
Specifies if the Dynamic Directory Services feature is enabled or not.
By default it is; however, a proxy does not need to keep track of dynamic
objects itself, it only needs to inform the frontend that support for
dynamic objects is available.
.SH ACCESS CONTROL
The
.B dds
overlay restricts the refresh operation by requiring
.B manage
access to the
.B entryTtl
attribute (see
.BR slapd.access (5)
for details about the
.B manage
access privilege).
Since the
.B entryTtl
is an operational, NO-USER-MODIFICATION attribute, no direct write access
to it is possible.
So the
.B dds
overlay turns refresh exops into an internal modification to the value
of the
.B entryTtl
attribute with the
.B manageDIT
control set.
RFC 2589 recommends that anonymous clients should not be allowed to refresh
a dynamic object.
This cn be implemented by appropriately crafting access control to obtain
the desired effect.
Example: restrict refresh to authenticated clients
.RS
.nf
access to attrs=entryTtl
by users manage
by * read
.fi
.RE
Example: restrict refresh to the creator of the dynamic object
.RS
.nf
access to attrs=entryTtl
by dnattr=creatorsName manage
by * read
.fi
.RE
Another suggested usage of dynamic objects is to implement dynamic meetings;
in this case, all the participants to the meeting are allowed to refresh
the meeting object, but only the creator can delete it (otherwise it will
be deleted when the TTL expires)
Example: assuming \fIparticipant\fP is a valid DN-valued attribute,
allow users to start a meeting and to join it; restrict refresh
to the participants; restrict delete to the creator
.RS
.nf
access to dn.base="cn=Meetings"
attrs=children
by users write
access to dn.onelevel="cn=Meetings"
attrs=entry
by dnattr=creatorsName write
by * read
access to dn.onelevel="cn=Meetings"
attrs=participant
by dnattr=creatorsName write
by users selfwrite
by * read
access to dn.onelevel="cn=Meetings"
attrs=entryTtl
by dnattr=participant manage
by * read
.fi
.RE
.SH REPLICATION
This implementation of RFC 2589 provides a restricted interpretation of how
dynamic objects replicate. Only the master takes care of handling dynamic
object expiration, while replicas simply see the dynamic object as a plain
object.
When using slurpd replication, one needs to explicitly exclude the
.B dynamicObject
class and the
.B entryTtl
attribute.
This implementation of RFC 2589 introduces a new operational attribute,
.BR entryExpireTimestamp ,