First-cut proxy authorization support.

This commit is contained in:
Kurt Zeilenga 2002-12-03 06:11:32 +00:00
parent 9ba95dd027
commit da76c1951e
19 changed files with 589 additions and 415 deletions

View File

@ -215,7 +215,7 @@ main( int argc, char **argv )
}
assert( authzid == NULL );
authzid = control;
authzid = cvalue;
} else if ( strcasecmp( control, "manageDSAit" ) == 0 ) {
if( manageDSAit ) {

View File

@ -214,7 +214,7 @@ main( int argc, char **argv )
}
assert( authzid == NULL );
authzid = control;
authzid = cvalue;
} else if ( strcasecmp( control, "manageDSAit" ) == 0 ) {
if( manageDSAit ) {

View File

@ -278,7 +278,7 @@ main( int argc, char **argv )
}
assert( authzid == NULL );
authzid = control;
authzid = cvalue;
} else if ( strcasecmp( control, "manageDSAit" ) == 0 ) {
if( manageDSAit ) {

View File

@ -234,7 +234,7 @@ main(int argc, char **argv)
}
assert( authzid == NULL );
authzid = control;
authzid = cvalue;
} else if ( strcasecmp( control, "manageDSAit" ) == 0 ) {
if( manageDSAit ) {

View File

@ -234,7 +234,7 @@ main( int argc, char *argv[] )
}
assert( authzid == NULL );
authzid = control;
authzid = cvalue;
} else if ( strcasecmp( control, "manageDSAit" ) == 0 ) {
if( manageDSAit ) {

View File

@ -471,7 +471,7 @@ main( int argc, char **argv )
}
assert( authzid == NULL );
authzid = control;
authzid = cvalue;
} else if ( strcasecmp( control, "manageDSAit" ) == 0 ) {
if( manageDSAit ) {

View File

@ -190,7 +190,7 @@ main( int argc, char *argv[] )
}
assert( authzid == NULL );
authzid = control;
authzid = cvalue;
} else if ( strcasecmp( control, "manageDSAit" ) == 0 ) {
if( manageDSAit ) {

View File

@ -413,8 +413,9 @@ typedef struct ldapcontrol {
#define LDAP_IS_LEAF 0x23 /* not LDAPv3 */
#define LDAP_ALIAS_DEREF_PROBLEM 0x24
#define LDAP_SECURITY_ERROR(n) LDAP_RANGE((n),0x30,0x32) /* 48-50 */
#define LDAP_SECURITY_ERROR(n) LDAP_RANGE((n),0x2F,0x32) /* 47-50 */
#define LDAP_PROXY_AUTHZ_FAILURE 0x2F /* LDAPv3 proxy authorization */
#define LDAP_INAPPROPRIATE_AUTH 0x30
#define LDAP_INVALID_CREDENTIALS 0x31
#define LDAP_INSUFFICIENT_ACCESS 0x32

View File

@ -53,6 +53,7 @@ static struct ldaperror ldap_builtin_errlist[] = {
{LDAP_IS_LEAF, "Entry is a leaf" },
{LDAP_ALIAS_DEREF_PROBLEM, "Alias dereferencing problem" },
{LDAP_PROXY_AUTHZ_FAILURE, "Proxy Authorization Failure" },
{LDAP_INAPPROPRIATE_AUTH, "Inappropriate authentication" },
{LDAP_INVALID_CREDENTIALS, "Invalid credentials" },
{LDAP_INSUFFICIENT_ACCESS, "Insufficient access" },

View File

@ -561,12 +561,10 @@ read_config( const char *fname, int depth )
lutil_salt_format( cargv[1] );
#ifdef HAVE_CYRUS_SASL
/* SASL config options */
} else if ( strncasecmp( cargv[0], "sasl", 4 ) == 0 ) {
if ( slap_sasl_config( cargc, cargv, line, fname, lineno ) )
return 1;
#endif /* HAVE_CYRUS_SASL */
} else if ( strcasecmp( cargv[0], "schemadn" ) == 0 ) {
struct berval dn;

View File

@ -44,18 +44,27 @@ typedef int (SLAP_CTRL_PARSE_FN) LDAP_P((
LDAPControl *ctrl,
const char **text ));
static SLAP_CTRL_PARSE_FN parseProxyAuthz;
static SLAP_CTRL_PARSE_FN parseManageDSAit;
static SLAP_CTRL_PARSE_FN parseSubentries;
static SLAP_CTRL_PARSE_FN parseNoOp;
static SLAP_CTRL_PARSE_FN parsePagedResults;
static SLAP_CTRL_PARSE_FN parseValuesReturnFilter;
#ifdef LDAP_CONTROL_SUBENTRIES
static SLAP_CTRL_PARSE_FN parseSubentries;
#endif
#ifdef LDAP_CLIENT_UPDATE
static SLAP_CTRL_PARSE_FN parseClientUpdate;
#endif /* LDAP_CLIENT_UPDATE */
#endif
#undef sc_mask /* avoid conflict with Irix 6.5 <sys/signal.h> */
static char *proxy_authz_extops[] = {
LDAP_EXOP_MODIFY_PASSWD,
LDAP_EXOP_X_WHO_AM_I,
NULL
};
static struct slap_control {
char *sc_oid;
slap_mask_t sc_mask;
@ -63,14 +72,12 @@ static struct slap_control {
SLAP_CTRL_PARSE_FN *sc_parse;
} supportedControls[] = {
{ LDAP_CONTROL_PROXY_AUTHZ,
SLAP_CTRL_FRONTEND|SLAP_CTRL_ACCESS, proxy_authz_extops,
parseProxyAuthz },
{ LDAP_CONTROL_MANAGEDSAIT,
SLAP_CTRL_ACCESS, NULL,
parseManageDSAit },
#ifdef LDAP_CONTROL_SUBENTRIES
{ LDAP_CONTROL_SUBENTRIES,
SLAP_CTRL_SEARCH, NULL,
parseSubentries },
#endif
{ LDAP_CONTROL_NOOP,
SLAP_CTRL_ACCESS, NULL,
parseNoOp },
@ -79,7 +86,12 @@ static struct slap_control {
parsePagedResults },
{ LDAP_CONTROL_VALUESRETURNFILTER,
SLAP_CTRL_SEARCH, NULL,
parseValuesReturnFilter },
parseValuesReturnFilter },
#ifdef LDAP_CONTROL_SUBENTRIES
{ LDAP_CONTROL_SUBENTRIES,
SLAP_CTRL_SEARCH, NULL,
parseSubentries },
#endif
#ifdef LDAP_CLIENT_UPDATE
{ LDAP_CONTROL_CLIENT_UPDATE,
SLAP_CTRL_SEARCH, NULL,
@ -316,8 +328,19 @@ int get_ctrls(
tagmask = SLAP_CTRL_ABANDON;
break;
case LDAP_REQ_EXTENDED:
/* FIXME: check list of extended operations */
tagmask = ~0U;
tagmask=~0L;
assert( op->o_extendedop != NULL );
if( sc->sc_extendedops != NULL ) {
int i;
for( i=0; sc->sc_extendedops[i] != NULL; i++ ) {
if( strcmp( op->o_extendedop, sc->sc_extendedops[i] )
== 0 )
{
tagmask=0L;
break;
}
}
}
break;
default:
rc = LDAP_OTHER;
@ -367,9 +390,11 @@ int get_ctrls(
return_results:
#ifdef NEW_LOGGING
LDAP_LOG( OPERATION, RESULTS,
"get_ctrls: n=%d rc=%d err=%s\n", nctrls, rc, errmsg ? errmsg : "" );
"get_ctrls: n=%d rc=%d err=\"%s\"\n",
nctrls, rc, errmsg ? errmsg : "" );
#else
Debug( LDAP_DEBUG_TRACE, "<= get_ctrls: n=%d rc=%d err=%s\n",
Debug( LDAP_DEBUG_TRACE,
"<= get_ctrls: n=%d rc=%d err=\"%s\"\n",
nctrls, rc, errmsg ? errmsg : "");
#endif
@ -408,37 +433,101 @@ static int parseManageDSAit (
return LDAP_SUCCESS;
}
#ifdef LDAP_CONTROL_SUBENTRIES
static int parseSubentries (
static int parseProxyAuthz (
Connection *conn,
Operation *op,
LDAPControl *ctrl,
const char **text )
{
if ( op->o_subentries != SLAP_NO_CONTROL ) {
*text = "subentries control specified multiple times";
int rc;
struct berval dn;
if ( op->o_proxy_authz != SLAP_NO_CONTROL ) {
*text = "proxy authorization control specified multiple times";
return LDAP_PROTOCOL_ERROR;
}
/* FIXME: should use BER library */
if( ( ctrl->ldctl_value.bv_len != 3 )
&& ( ctrl->ldctl_value.bv_val[0] != 0x01 )
&& ( ctrl->ldctl_value.bv_val[1] != 0x01 ))
{
*text = "subentries control value encoding is bogus";
return LDAP_PROTOCOL_ERROR;
}
op->o_subentries = ctrl->ldctl_iscritical
op->o_proxy_authz = ctrl->ldctl_iscritical
? SLAP_CRITICAL_CONTROL
: SLAP_NONCRITICAL_CONTROL;
op->o_subentries_visibility = (ctrl->ldctl_value.bv_val[2] != 0x00);
return LDAP_SUCCESS;
}
#ifdef NEW_LOGGING
LDAP_LOG( OPERATION, ARGS,
"parseProxyAuthz: conn %d authzid=\"%s\"\n",
conn->c_connid,
ctrl->ldctl_value.bv_len ? ctrl->ldctl_value.bv_val : "anonymous",
0 );
#else
Debug( LDAP_DEBUG_ARGS,
"parseProxyAuthz: conn %d authzid=\"%s\"\n",
conn->c_connid,
ctrl->ldctl_value.bv_len ? ctrl->ldctl_value.bv_val : "anonymous",
0 );
#endif
if( ctrl->ldctl_value.bv_len == 0 ) {
#ifdef NEW_LOGGING
LDAP_LOG( OPERATION, TRACE,
"parseProxyAuthz: conn=%d anonymous\n",
conn->c_connid, 0, 0 );
#else
Debug( LDAP_DEBUG_TRACE,
"parseProxyAuthz: conn=%d anonymous\n",
conn->c_connid, 0, 0 );
#endif
/* anonymous */
free( op->o_dn.bv_val );
op->o_dn.bv_len = 0;
op->o_dn.bv_val = ch_strdup( "" );
free( op->o_ndn.bv_val );
op->o_ndn.bv_len = 0;
op->o_ndn.bv_val = ch_strdup( "" );
return LDAP_SUCCESS;
}
rc = slap_sasl_getdn( conn,
ctrl->ldctl_value.bv_val, ctrl->ldctl_value.bv_len,
NULL, &dn, SLAP_GETDN_AUTHZID );
if( rc != LDAP_SUCCESS || !dn.bv_len ) {
*text = "authzId mapping failed";
return LDAP_PROXY_AUTHZ_FAILURE;
}
#ifdef NEW_LOGGING
LDAP_LOG( OPERATION, TRACE,
"parseProxyAuthz: conn=%d \"%s\"\n",
conn->c_connid,
dn.bv_len ? dn.bv_val : "(NULL)", 0 );
#else
Debug( LDAP_DEBUG_TRACE,
"parseProxyAuthz: conn=%d \"%s\"\n",
conn->c_connid,
dn.bv_len ? dn.bv_val : "(NULL)", 0 );
#endif
rc = slap_sasl_authorized( conn, &op->o_dn, &dn );
if( rc ) {
ch_free( dn.bv_val );
*text = "not authorized to assume identity";
return LDAP_PROXY_AUTHZ_FAILURE;
}
#if 0
ch_free( op->o_dn );
ch_free( op->o_ndn );
op->o_dn = dn;
#endif
*text = "not (yet) implemented";
return LDAP_OTHER;
}
static int parseNoOp (
Connection *conn,
Operation *op,
@ -479,7 +568,7 @@ static int parsePagedResults (
}
if ( ctrl->ldctl_value.bv_len == 0 ) {
*text = "paged results control value is empty";
*text = "paged results control value is empty (or absent)";
return LDAP_PROTOCOL_ERROR;
}
@ -555,7 +644,12 @@ int parseValuesReturnFilter (
const char *err_msg = "";
if ( op->o_valuesreturnfilter != SLAP_NO_CONTROL ) {
*text = "valuesreturnfilter control specified multiple times";
*text = "valuesReturnFilter control specified multiple times";
return LDAP_PROTOCOL_ERROR;
}
if ( ctrl->ldctl_value.bv_len == 0 ) {
*text = "valuesReturnFilter control value is empty (or absent)";
return LDAP_PROTOCOL_ERROR;
}
@ -599,6 +693,37 @@ int parseValuesReturnFilter (
return LDAP_SUCCESS;
}
#ifdef LDAP_CONTROL_SUBENTRIES
static int parseSubentries (
Connection *conn,
Operation *op,
LDAPControl *ctrl,
const char **text )
{
if ( op->o_subentries != SLAP_NO_CONTROL ) {
*text = "subentries control specified multiple times";
return LDAP_PROTOCOL_ERROR;
}
/* FIXME: should use BER library */
if( ( ctrl->ldctl_value.bv_len != 3 )
&& ( ctrl->ldctl_value.bv_val[0] != 0x01 )
&& ( ctrl->ldctl_value.bv_val[1] != 0x01 ))
{
*text = "subentries control value encoding is bogus";
return LDAP_PROTOCOL_ERROR;
}
op->o_subentries = ctrl->ldctl_iscritical
? SLAP_CRITICAL_CONTROL
: SLAP_NONCRITICAL_CONTROL;
op->o_subentries_visibility = (ctrl->ldctl_value.bv_val[2] != 0x00);
return LDAP_SUCCESS;
}
#endif
#ifdef LDAP_CLIENT_UPDATE
static int parseClientUpdate (
Connection *conn,
@ -620,7 +745,7 @@ static int parseClientUpdate (
}
if ( ctrl->ldctl_value.bv_len == 0 ) {
*text = "LCUP client update control value is empty";
*text = "LCUP client update control value is empty (or absent)";
return LDAP_PROTOCOL_ERROR;
}

View File

@ -150,6 +150,8 @@ do_extended(
goto done;
}
op->o_extendedop = reqoid.bv_val;
tag = ber_peek_tag( op->o_ber, &len );
if( ber_peek_tag( op->o_ber, &len ) == LDAP_TAG_EXOP_REQ_VALUE ) {

View File

@ -875,6 +875,9 @@ LDAP_SLAPD_F (int) slap_sasl_config(
const char *fname,
int lineno );
LDAP_SLAPD_F (int) slap_sasl_getdn( Connection *conn,
char *id, int len,
char *user_realm, struct berval *dn, int flags );
/*
* saslauthz.c

View File

@ -447,7 +447,7 @@ send_ldap_result(
#ifdef NEW_LOGGING
LDAP_LOG( OPERATION, ENTRY,
"send_ldap_result : conn %lu op=%lu p=%d\n",
"send_ldap_result: conn %lu op=%lu p=%d\n",
op->o_connid, op->o_opid, op->o_protocol );
#else
Debug( LDAP_DEBUG_TRACE,
@ -567,13 +567,12 @@ send_ldap_extended(
rspdata != NULL ? rspdata->bv_len : 0 );
#else
Debug( LDAP_DEBUG_TRACE,
"send_ldap_extended err=%d oid=%s len=%ld\n",
"send_ldap_extended: err=%d oid=%s len=%ld\n",
err,
rspoid ? rspoid : "",
rspdata != NULL ? rspdata->bv_len : 0 );
#endif
tag = req2res( op->o_tag );
msgid = (tag != LBER_SEQUENCE) ? op->o_msgid : 0;

View File

@ -15,31 +15,28 @@
#include "slap.h"
#ifdef HAVE_CYRUS_SASL
#include <limits.h>
#ifdef HAVE_SASL_SASL_H
#include <sasl/sasl.h>
#else
#include <sasl.h>
#endif
#ifdef HAVE_CYRUS_SASL
# ifdef HAVE_SASL_SASL_H
# include <sasl/sasl.h>
# else
# include <sasl.h>
# endif
#include <lutil.h>
#if SASL_VERSION_MAJOR >= 2
#include <sasl/saslplug.h>
#define SASL_CONST const
#else
#define SASL_CONST
#endif
# if SASL_VERSION_MAJOR >= 2
# include <sasl/saslplug.h>
# define SASL_CONST const
# else
# define SASL_CONST
# endif
static sasl_security_properties_t sasl_secprops;
#endif /* HAVE_CYRUS_SASL */
#include "ldap_pvt.h"
#include "lber_pvt.h"
/* Flags for telling slap_sasl_getdn() what type of identity is being passed */
#define FLAG_GETDN_AUTHCID 2
#define FLAG_GETDN_AUTHZID 4
static sasl_security_properties_t sasl_secprops;
#include <lutil.h>
int slap_sasl_config( int cargc, char **cargv, char *line,
const char *fname, int lineno )
@ -49,11 +46,13 @@ int slap_sasl_config( int cargc, char **cargv, char *line,
if ( cargc != 2 ) {
#ifdef NEW_LOGGING
LDAP_LOG( CONFIG, CRIT,
"%s: line %d: missing policy in \"sasl-authz-policy <policy>\" line\n",
fname, lineno, 0 );
"%s: line %d: missing policy in"
" \"sasl-authz-policy <policy>\" line\n",
fname, lineno, 0 );
#else
Debug( LDAP_DEBUG_ANY,
"%s: line %d: missing policy in \"sasl-authz-policy <policy>\" line\n",
"%s: line %d: missing policy in"
" \"sasl-authz-policy <policy>\" line\n",
fname, lineno, 0 );
#endif
@ -77,18 +76,42 @@ int slap_sasl_config( int cargc, char **cargv, char *line,
#endif
return( 1 );
}
} else if ( !strcasecmp( cargv[0], "sasl-regexp" )
|| !strcasecmp( cargv[0], "saslregexp" ) )
{
int rc;
if ( cargc != 3 ) {
#ifdef NEW_LOGGING
LDAP_LOG( CONFIG, CRIT,
"%s: line %d: need 2 args in "
"\"saslregexp <match> <replace>\"\n",
fname, lineno, 0 );
#else
Debug( LDAP_DEBUG_ANY,
"%s: line %d: need 2 args in "
"\"saslregexp <match> <replace>\"\n",
fname, lineno, 0 );
#endif
return( 1 );
}
rc = slap_sasl_regexp_config( cargv[1], cargv[2] );
if ( rc ) {
return rc;
}
#ifdef HAVE_CYRUS_SASL
/* set SASL host */
} else if ( strcasecmp( cargv[0], "sasl-host" ) == 0 ) {
if ( cargc < 2 ) {
#ifdef NEW_LOGGING
LDAP_LOG( CONFIG, CRIT,
"%s: line %d: missing host in \"sasl-host <host>\" line\n",
fname, lineno, 0 );
"%s: line %d: missing host in \"sasl-host <host>\" line\n",
fname, lineno, 0 );
#else
Debug( LDAP_DEBUG_ANY,
"%s: line %d: missing host in \"sasl-host <host>\" line\n",
"%s: line %d: missing host in \"sasl-host <host>\" line\n",
fname, lineno, 0 );
#endif
@ -98,8 +121,8 @@ int slap_sasl_config( int cargc, char **cargv, char *line,
if ( global_host != NULL ) {
#ifdef NEW_LOGGING
LDAP_LOG( CONFIG, CRIT,
"%s: line %d: already set sasl-host!\n",
fname, lineno, 0 );
"%s: line %d: already set sasl-host!\n",
fname, lineno, 0 );
#else
Debug( LDAP_DEBUG_ANY,
"%s: line %d: already set sasl-host!\n",
@ -116,12 +139,12 @@ int slap_sasl_config( int cargc, char **cargv, char *line,
} else if ( strcasecmp( cargv[0], "sasl-realm" ) == 0 ) {
if ( cargc < 2 ) {
#ifdef NEW_LOGGING
LDAP_LOG( CONFIG, CRIT,
"%s: line %d: missing realm in \"sasl-realm <realm>\" line.\n",
fname, lineno, 0 );
LDAP_LOG( CONFIG, CRIT, "%s: line %d: "
"missing realm in \"sasl-realm <realm>\" line.\n",
fname, lineno, 0 );
#else
Debug( LDAP_DEBUG_ANY,
"%s: line %d: missing realm in \"sasl-realm <realm>\" line\n",
Debug( LDAP_DEBUG_ANY, "%s: line %d: "
"missing realm in \"sasl-realm <realm>\" line.\n",
fname, lineno, 0 );
#endif
@ -131,8 +154,8 @@ int slap_sasl_config( int cargc, char **cargv, char *line,
if ( global_realm != NULL ) {
#ifdef NEW_LOGGING
LDAP_LOG( CONFIG, CRIT,
"%s: line %d: already set sasl-realm!\n",
fname, lineno, 0 );
"%s: line %d: already set sasl-realm!\n",
fname, lineno, 0 );
#else
Debug( LDAP_DEBUG_ANY,
"%s: line %d: already set sasl-realm!\n",
@ -145,42 +168,18 @@ int slap_sasl_config( int cargc, char **cargv, char *line,
global_realm = ch_strdup( cargv[1] );
}
} else if ( !strcasecmp( cargv[0], "sasl-regexp" )
|| !strcasecmp( cargv[0], "saslregexp" ) )
{
int rc;
if ( cargc != 3 ) {
#ifdef NEW_LOGGING
LDAP_LOG( CONFIG, CRIT,
"%s: line %d: need 2 args in "
"\"saslregexp <match> <replace>\"\n",
fname, lineno, 0 );
#else
Debug( LDAP_DEBUG_ANY,
"%s: line %d: need 2 args in \"saslregexp <match> <replace>\"\n",
fname, lineno, 0 );
#endif
return( 1 );
}
rc = slap_sasl_regexp_config( cargv[1], cargv[2] );
if ( rc ) {
return rc;
}
/* SASL security properties */
} else if ( strcasecmp( cargv[0], "sasl-secprops" ) == 0 ) {
char *txt;
if ( cargc < 2 ) {
#ifdef NEW_LOGGING
LDAP_LOG( CONFIG, CRIT,
"%s: line %d: missing flags in "
"\"sasl-secprops <properties>\" line\n",
fname, lineno, 0 );
LDAP_LOG( CONFIG, CRIT, "%s: line %d: "
"missing flags in \"sasl-secprops <properties>\" line\n",
fname, lineno, 0 );
#else
Debug( LDAP_DEBUG_ANY,
"%s: line %d: missing flags in \"sasl-secprops <properties>\" line\n",
Debug( LDAP_DEBUG_ANY, "%s: line %d: "
"missing flags in \"sasl-secprops <properties>\" line\n",
fname, lineno, 0 );
#endif
@ -191,22 +190,25 @@ int slap_sasl_config( int cargc, char **cargv, char *line,
if ( txt != NULL ) {
#ifdef NEW_LOGGING
LDAP_LOG( CONFIG, CRIT,
"%s: line %d sasl-secprops: %s\n",
fname, lineno, txt );
"%s: line %d sasl-secprops: %s\n",
fname, lineno, txt );
#else
Debug( LDAP_DEBUG_ANY,
"%s: line %d: sasl-secprops: %s\n",
"%s: line %d: sasl-secprops: %s\n",
fname, lineno, txt );
#endif
return 1;
}
#endif /* HAVE_CYRUS_SASL */
}
return LDAP_SUCCESS;
}
static int
#ifdef HAVE_CYRUS_SASL
int
slap_sasl_log(
void *context,
int priority,
@ -286,190 +288,6 @@ slap_sasl_log(
}
/* Take any sort of identity string and return a DN with the "dn:" prefix. The
string returned in *dn is in its own allocated memory, and must be free'd
by the calling process.
-Mark Adamson, Carnegie Mellon
The "dn:" prefix is no longer used anywhere inside slapd. It is only used
on strings passed in directly from SASL.
-Howard Chu, Symas Corp.
*/
#define SET_DN 1
#define SET_U 2
static struct berval ext_bv = BER_BVC( "EXTERNAL" );
int slap_sasl_getdn( Connection *conn, char *id, int len,
char *user_realm, struct berval *dn, int flags )
{
char *c1;
int rc, is_dn = 0, do_norm = 1;
sasl_conn_t *ctx;
struct berval dn2;
#ifdef NEW_LOGGING
LDAP_LOG( TRANSPORT, ENTRY,
"slap_sasl_getdn: conn %d id=%s\n",
conn ? conn->c_connid : -1, id ? (*id ? id : "<empty>") : "NULL", 0 );
#else
Debug( LDAP_DEBUG_ARGS, "slap_sasl_getdn: id=%s\n",
id?(*id?id:"<empty>"):"NULL",0,0 );
#endif
dn->bv_val = NULL;
dn->bv_len = 0;
if ( id ) {
if ( len == 0 ) len = strlen( id );
/* Blatantly anonymous ID */
if ( len == sizeof("anonymous") - 1 &&
!strcasecmp( id, "anonymous" ) ) {
return( LDAP_SUCCESS );
}
} else {
len = 0;
}
ctx = conn->c_sasl_context;
/* An authcID needs to be converted to authzID form. Set the
* values directly into *dn; they will be normalized later. (and
* normalizing always makes a new copy.) An ID from a TLS certificate
* is already normalized, so copy it and skip normalization.
*/
if( flags & FLAG_GETDN_AUTHCID ) {
#ifdef HAVE_TLS
if( conn->c_is_tls &&
conn->c_sasl_bind_mech.bv_len == ext_bv.bv_len &&
strcasecmp( ext_bv.bv_val, conn->c_sasl_bind_mech.bv_val ) == 0 )
{
/* X.509 DN is already normalized */
do_norm = 0;
is_dn = SET_DN;
ber_str2bv( id, len, 1, dn );
} else
#endif
{
/* convert to u:<username> form */
is_dn = SET_U;
dn->bv_val = id;
dn->bv_len = len;
}
}
if( !is_dn ) {
if( !strncasecmp( id, "u:", sizeof("u:")-1 )) {
is_dn = SET_U;
dn->bv_val = id+2;
dn->bv_len = len-2;
} else if ( !strncasecmp( id, "dn:", sizeof("dn:")-1) ) {
is_dn = SET_DN;
dn->bv_val = id+3;
dn->bv_len = len-3;
}
}
/* No other possibilities from here */
if( !is_dn ) {
dn->bv_val = NULL;
dn->bv_len = 0;
return( LDAP_INAPPROPRIATE_AUTH );
}
/* Username strings */
if( is_dn == SET_U ) {
char *p, *realm;
len = dn->bv_len + sizeof("uid=")-1 + sizeof(",cn=auth")-1;
/* username may have embedded realm name */
if( ( realm = strchr( dn->bv_val, '@') ) ) {
*realm++ = '\0';
len += sizeof(",cn=")-2;
} else if( user_realm && *user_realm ) {
len += strlen( user_realm ) + sizeof(",cn=")-1;
}
if( conn->c_sasl_bind_mech.bv_len ) {
len += conn->c_sasl_bind_mech.bv_len + sizeof(",cn=")-1;
}
/* Build the new dn */
c1 = dn->bv_val;
dn->bv_val = SLAP_MALLOC( len+1 );
if( dn->bv_val == NULL ) {
#ifdef NEW_LOGGING
LDAP_LOG( TRANSPORT, ERR,
"slap_sasl_getdn: SLAP_MALLOC failed", 0, 0, 0 );
#else
Debug( LDAP_DEBUG_ANY,
"slap_sasl_getdn: SLAP_MALLOC failed", 0, 0, 0 );
#endif
return LDAP_OTHER;
}
p = lutil_strcopy( dn->bv_val, "uid=" );
p = lutil_strncopy( p, c1, dn->bv_len );
if( realm ) {
int rlen = dn->bv_len - ( realm - c1 );
p = lutil_strcopy( p, ",cn=" );
p = lutil_strncopy( p, realm, rlen );
realm[-1] = '@';
} else if( user_realm && *user_realm ) {
p = lutil_strcopy( p, ",cn=" );
p = lutil_strcopy( p, user_realm );
}
if( conn->c_sasl_bind_mech.bv_len ) {
p = lutil_strcopy( p, ",cn=" );
p = lutil_strcopy( p, conn->c_sasl_bind_mech.bv_val );
}
p = lutil_strcopy( p, ",cn=auth" );
dn->bv_len = p - dn->bv_val;
#ifdef NEW_LOGGING
LDAP_LOG( TRANSPORT, ENTRY,
"slap_sasl_getdn: u:id converted to %s.\n", dn->bv_val, 0, 0 );
#else
Debug( LDAP_DEBUG_TRACE, "getdn: u:id converted to %s\n", dn->bv_val,0,0 );
#endif
}
/* All strings are in DN form now. Normalize if needed. */
if ( do_norm ) {
rc = dnNormalize2( NULL, dn, &dn2 );
/* User DNs were constructed above and must be freed now */
if ( is_dn == SET_U )
ch_free( dn->bv_val );
if ( rc != LDAP_SUCCESS ) {
dn->bv_val = NULL;
dn->bv_len = 0;
return rc;
}
*dn = dn2;
}
/* Run thru regexp */
slap_sasl2dn( conn, dn, &dn2 );
if( dn2.bv_val ) {
ch_free( dn->bv_val );
*dn = dn2;
#ifdef NEW_LOGGING
LDAP_LOG( TRANSPORT, ENTRY,
"slap_sasl_getdn: dn:id converted to %s.\n", dn->bv_val, 0, 0 );
#else
Debug( LDAP_DEBUG_TRACE, "getdn: dn:id converted to %s\n",
dn->bv_val, 0, 0 );
#endif
}
return( LDAP_SUCCESS );
}
#if SASL_VERSION_MAJOR >= 2
static const char *slap_propnames[] = {
"*slapConn", "*authcDN", "*authzDN", NULL };
@ -714,7 +532,7 @@ slap_sasl_checkpass(
*/
rc = slap_sasl_getdn( conn, (char *)username, 0, NULL, &dn,
FLAG_GETDN_AUTHCID );
SLAP_GETDN_AUTHCID );
if ( rc != LDAP_SUCCESS ) {
sasl_seterror( sconn, 0, ldap_err2string( rc ) );
return SASL_NOUSER;
@ -849,7 +667,7 @@ slap_sasl_canonicalize(
}
rc = slap_sasl_getdn( conn, (char *)in, inlen, (char *)user_realm, &dn,
(flags & SASL_CU_AUTHID) ? FLAG_GETDN_AUTHCID : FLAG_GETDN_AUTHZID );
(flags & SASL_CU_AUTHID) ? SLAP_GETDN_AUTHCID : SLAP_GETDN_AUTHZID );
if ( rc != LDAP_SUCCESS ) {
sasl_seterror( sconn, 0, ldap_err2string( rc ) );
return SASL_NOAUTHZ;
@ -1004,7 +822,8 @@ slap_sasl_authorize(
/* Convert the identities to DN's. If no authzid was given, client will
be bound as the DN matching their username */
rc = slap_sasl_getdn( conn, (char *)authcid, 0, realm, &authcDN, FLAG_GETDN_AUTHCID );
rc = slap_sasl_getdn( conn, (char *)authcid, 0, realm,
&authcDN, SLAP_GETDN_AUTHCID );
if( rc != LDAP_SUCCESS ) {
*errstr = ldap_err2string( rc );
return SASL_NOAUTHZ;
@ -1023,7 +842,8 @@ slap_sasl_authorize(
*errstr = NULL;
return SASL_OK;
}
rc = slap_sasl_getdn( conn, (char *)authzid, 0, realm, &authzDN, FLAG_GETDN_AUTHZID );
rc = slap_sasl_getdn( conn, (char *)authzid, 0, realm,
&authzDN, SLAP_GETDN_AUTHZID );
if( rc != LDAP_SUCCESS ) {
ch_free( authcDN.bv_val );
*errstr = ldap_err2string( rc );
@ -1101,7 +921,6 @@ slap_sasl_err2ldap( int saslerr )
}
#endif
int slap_sasl_init( void )
{
#ifdef HAVE_CYRUS_SASL
@ -1677,4 +1496,185 @@ slap_sasl_setpass(
done:
return rc;
}
#endif /* HAVE_CYRUS_SASL */
/* Take any sort of identity string and return a DN with the "dn:" prefix. The
string returned in *dn is in its own allocated memory, and must be free'd
by the calling process.
-Mark Adamson, Carnegie Mellon
The "dn:" prefix is no longer used anywhere inside slapd. It is only used
on strings passed in directly from SASL.
-Howard Chu, Symas Corp.
*/
#define SET_DN 1
#define SET_U 2
static struct berval ext_bv = BER_BVC( "EXTERNAL" );
int slap_sasl_getdn( Connection *conn, char *id, int len,
char *user_realm, struct berval *dn, int flags )
{
char *c1;
int rc, is_dn = 0, do_norm = 1;
struct berval dn2;
#ifdef NEW_LOGGING
LDAP_LOG( TRANSPORT, ENTRY,
"slap_sasl_getdn: conn %d id=%s\n",
conn ? conn->c_connid : -1, id ? (*id ? id : "<empty>") : "NULL", 0 );
#else
Debug( LDAP_DEBUG_ARGS, "slap_sasl_getdn: id=%s\n",
id?(*id?id:"<empty>"):"NULL",0,0 );
#endif
dn->bv_val = NULL;
dn->bv_len = 0;
if ( id ) {
if ( len == 0 ) len = strlen( id );
/* Blatantly anonymous ID */
if ( len == sizeof("anonymous") - 1 &&
!strcasecmp( id, "anonymous" ) ) {
return( LDAP_SUCCESS );
}
} else {
len = 0;
}
/* An authcID needs to be converted to authzID form. Set the
* values directly into *dn; they will be normalized later. (and
* normalizing always makes a new copy.) An ID from a TLS certificate
* is already normalized, so copy it and skip normalization.
*/
if( flags & SLAP_GETDN_AUTHCID ) {
#ifdef HAVE_TLS
if( conn->c_is_tls &&
conn->c_sasl_bind_mech.bv_len == ext_bv.bv_len &&
strcasecmp( ext_bv.bv_val, conn->c_sasl_bind_mech.bv_val ) == 0 )
{
/* X.509 DN is already normalized */
do_norm = 0;
is_dn = SET_DN;
ber_str2bv( id, len, 1, dn );
} else
#endif
{
/* convert to u:<username> form */
is_dn = SET_U;
dn->bv_val = id;
dn->bv_len = len;
}
}
if( !is_dn ) {
if( !strncasecmp( id, "u:", sizeof("u:")-1 )) {
is_dn = SET_U;
dn->bv_val = id+2;
dn->bv_len = len-2;
} else if ( !strncasecmp( id, "dn:", sizeof("dn:")-1) ) {
is_dn = SET_DN;
dn->bv_val = id+3;
dn->bv_len = len-3;
}
}
/* No other possibilities from here */
if( !is_dn ) {
dn->bv_val = NULL;
dn->bv_len = 0;
return( LDAP_INAPPROPRIATE_AUTH );
}
/* Username strings */
if( is_dn == SET_U ) {
char *p, *realm;
len = dn->bv_len + sizeof("uid=")-1 + sizeof(",cn=auth")-1;
/* username may have embedded realm name */
if( ( realm = strchr( dn->bv_val, '@') ) ) {
*realm++ = '\0';
len += sizeof(",cn=")-2;
} else if( user_realm && *user_realm ) {
len += strlen( user_realm ) + sizeof(",cn=")-1;
}
if( conn->c_sasl_bind_mech.bv_len ) {
len += conn->c_sasl_bind_mech.bv_len + sizeof(",cn=")-1;
}
/* Build the new dn */
c1 = dn->bv_val;
dn->bv_val = SLAP_MALLOC( len+1 );
if( dn->bv_val == NULL ) {
#ifdef NEW_LOGGING
LDAP_LOG( TRANSPORT, ERR,
"slap_sasl_getdn: SLAP_MALLOC failed", 0, 0, 0 );
#else
Debug( LDAP_DEBUG_ANY,
"slap_sasl_getdn: SLAP_MALLOC failed", 0, 0, 0 );
#endif
return LDAP_OTHER;
}
p = lutil_strcopy( dn->bv_val, "uid=" );
p = lutil_strncopy( p, c1, dn->bv_len );
if( realm ) {
int rlen = dn->bv_len - ( realm - c1 );
p = lutil_strcopy( p, ",cn=" );
p = lutil_strncopy( p, realm, rlen );
realm[-1] = '@';
} else if( user_realm && *user_realm ) {
p = lutil_strcopy( p, ",cn=" );
p = lutil_strcopy( p, user_realm );
}
if( conn->c_sasl_bind_mech.bv_len ) {
p = lutil_strcopy( p, ",cn=" );
p = lutil_strcopy( p, conn->c_sasl_bind_mech.bv_val );
}
p = lutil_strcopy( p, ",cn=auth" );
dn->bv_len = p - dn->bv_val;
#ifdef NEW_LOGGING
LDAP_LOG( TRANSPORT, ENTRY,
"slap_sasl_getdn: u:id converted to %s.\n", dn->bv_val, 0, 0 );
#else
Debug( LDAP_DEBUG_TRACE, "getdn: u:id converted to %s\n", dn->bv_val,0,0 );
#endif
}
/* All strings are in DN form now. Normalize if needed. */
if ( do_norm ) {
rc = dnNormalize2( NULL, dn, &dn2 );
/* User DNs were constructed above and must be freed now */
if ( is_dn == SET_U )
ch_free( dn->bv_val );
if ( rc != LDAP_SUCCESS ) {
dn->bv_val = NULL;
dn->bv_len = 0;
return rc;
}
*dn = dn2;
}
/* Run thru regexp */
slap_sasl2dn( conn, dn, &dn2 );
if( dn2.bv_val ) {
ch_free( dn->bv_val );
*dn = dn2;
#ifdef NEW_LOGGING
LDAP_LOG( TRANSPORT, ENTRY,
"slap_sasl_getdn: dn:id converted to %s.\n", dn->bv_val, 0, 0 );
#else
Debug( LDAP_DEBUG_TRACE, "getdn: dn:id converted to %s\n",
dn->bv_val, 0, 0 );
#endif
}
return( LDAP_SUCCESS );
}

View File

@ -23,15 +23,8 @@
#include "slap.h"
#ifdef HAVE_CYRUS_SASL
#include <limits.h>
#ifdef HAVE_SASL_SASL_H
#include <sasl/sasl.h>
#else
#include <sasl.h>
#endif
#include <ldap_pvt.h>
#define SASLREGEX_REPLACE 10
@ -372,107 +365,6 @@ static int sasl_sc_sasl2dn( BackendDB *be, Connection *conn, Operation *o,
return 0;
}
/*
* Given a SASL name (e.g. "UID=name,cn=REALM,cn=MECH,cn=AUTH")
* return the LDAP DN to which it matches. The SASL regexp rules in the config
* file turn the SASL name into an LDAP URI. If the URI is just a DN (or a
* search with scope=base), just return the URI (or its searchbase). Otherwise
* an internal search must be done, and if that search returns exactly one
* entry, return the DN of that one entry.
*/
void slap_sasl2dn( Connection *conn,
struct berval *saslname, struct berval *sasldn )
{
int rc;
Backend *be = NULL;
struct berval dn = { 0, NULL };
int scope = LDAP_SCOPE_BASE;
Filter *filter = NULL;
slap_callback cb = {slap_cb_null_response, slap_cb_null_sresult, sasl_sc_sasl2dn, NULL};
Operation op = {0};
struct berval regout = { 0, NULL };
#ifdef NEW_LOGGING
LDAP_LOG( TRANSPORT, ENTRY,
"slap_sasl2dn: converting SASL name %s to DN.\n",
saslname->bv_val, 0, 0 );
#else
Debug( LDAP_DEBUG_TRACE, "==>slap_sasl2dn: "
"converting SASL name %s to a DN\n",
saslname->bv_val, 0,0 );
#endif
sasldn->bv_val = NULL;
sasldn->bv_len = 0;
cb.sc_private = sasldn;
/* Convert the SASL name into a minimal URI */
if( !slap_sasl_regexp( saslname, &regout ) ) {
goto FINISHED;
}
rc = slap_parseURI( &regout, &dn, &scope, &filter );
if( rc != LDAP_SUCCESS ) {
goto FINISHED;
}
/* Must do an internal search */
be = select_backend( &dn, 0, 1 );
/* Massive shortcut: search scope == base */
if( scope == LDAP_SCOPE_BASE ) {
*sasldn = dn;
dn.bv_len = 0;
dn.bv_val = NULL;
goto FINISHED;
}
#ifdef NEW_LOGGING
LDAP_LOG( TRANSPORT, DETAIL1,
"slap_sasl2dn: performing internal search (base=%s, scope=%d)\n",
dn.bv_val, scope, 0 );
#else
Debug( LDAP_DEBUG_TRACE,
"slap_sasl2dn: performing internal search (base=%s, scope=%d)\n",
dn.bv_val, scope, 0 );
#endif
if(( be == NULL ) || ( be->be_search == NULL)) {
goto FINISHED;
}
suffix_alias( be, &dn );
op.o_tag = LDAP_REQ_SEARCH;
op.o_protocol = LDAP_VERSION3;
op.o_ndn = *saslname;
op.o_callback = &cb;
op.o_time = slap_get_time();
op.o_do_not_cache = 1;
op.o_threadctx = conn->c_sasl_bindop->o_threadctx;
(*be->be_search)( be, conn, &op, NULL, &dn,
scope, LDAP_DEREF_NEVER, 1, 0,
filter, NULL, NULL, 1 );
FINISHED:
if( sasldn->bv_len ) {
conn->c_authz_backend = be;
}
if( dn.bv_len ) ch_free( dn.bv_val );
if( filter ) filter_free( filter );
#ifdef NEW_LOGGING
LDAP_LOG( TRANSPORT, ENTRY,
"slap_sasl2dn: Converted SASL name to %s\n",
sasldn->bv_len ? sasldn->bv_val : "<nothing>", 0, 0 );
#else
Debug( LDAP_DEBUG_TRACE, "<==slap_sasl2dn: Converted SASL name to %s\n",
sasldn->bv_len ? sasldn->bv_val : "<nothing>", 0, 0 );
#endif
return;
}
typedef struct smatch_info {
struct berval *dn;
@ -652,7 +544,108 @@ COMPLETE:
return( rc );
}
#endif /* HAVE_CYRUS_SASL */
/*
* Given a SASL name (e.g. "UID=name,cn=REALM,cn=MECH,cn=AUTH")
* return the LDAP DN to which it matches. The SASL regexp rules in the config
* file turn the SASL name into an LDAP URI. If the URI is just a DN (or a
* search with scope=base), just return the URI (or its searchbase). Otherwise
* an internal search must be done, and if that search returns exactly one
* entry, return the DN of that one entry.
*/
void slap_sasl2dn( Connection *conn,
struct berval *saslname, struct berval *sasldn )
{
int rc;
Backend *be = NULL;
struct berval dn = { 0, NULL };
int scope = LDAP_SCOPE_BASE;
Filter *filter = NULL;
slap_callback cb = { slap_cb_null_response,
slap_cb_null_sresult, sasl_sc_sasl2dn, NULL};
Operation op = {0};
struct berval regout = { 0, NULL };
#ifdef NEW_LOGGING
LDAP_LOG( TRANSPORT, ENTRY,
"slap_sasl2dn: converting SASL name %s to DN.\n",
saslname->bv_val, 0, 0 );
#else
Debug( LDAP_DEBUG_TRACE, "==>slap_sasl2dn: "
"converting SASL name %s to a DN\n",
saslname->bv_val, 0,0 );
#endif
sasldn->bv_val = NULL;
sasldn->bv_len = 0;
cb.sc_private = sasldn;
/* Convert the SASL name into a minimal URI */
if( !slap_sasl_regexp( saslname, &regout ) ) {
goto FINISHED;
}
rc = slap_parseURI( &regout, &dn, &scope, &filter );
if( rc != LDAP_SUCCESS ) {
goto FINISHED;
}
/* Must do an internal search */
be = select_backend( &dn, 0, 1 );
/* Massive shortcut: search scope == base */
if( scope == LDAP_SCOPE_BASE ) {
*sasldn = dn;
dn.bv_len = 0;
dn.bv_val = NULL;
goto FINISHED;
}
#ifdef NEW_LOGGING
LDAP_LOG( TRANSPORT, DETAIL1,
"slap_sasl2dn: performing internal search (base=%s, scope=%d)\n",
dn.bv_val, scope, 0 );
#else
Debug( LDAP_DEBUG_TRACE,
"slap_sasl2dn: performing internal search (base=%s, scope=%d)\n",
dn.bv_val, scope, 0 );
#endif
if(( be == NULL ) || ( be->be_search == NULL)) {
goto FINISHED;
}
suffix_alias( be, &dn );
op.o_tag = LDAP_REQ_SEARCH;
op.o_protocol = LDAP_VERSION3;
op.o_ndn = *saslname;
op.o_callback = &cb;
op.o_time = slap_get_time();
op.o_do_not_cache = 1;
op.o_threadctx = conn->c_sasl_bindop->o_threadctx;
(*be->be_search)( be, conn, &op, NULL, &dn,
scope, LDAP_DEREF_NEVER, 1, 0,
filter, NULL, NULL, 1 );
FINISHED:
if( sasldn->bv_len ) {
conn->c_authz_backend = be;
}
if( dn.bv_len ) ch_free( dn.bv_val );
if( filter ) filter_free( filter );
#ifdef NEW_LOGGING
LDAP_LOG( TRANSPORT, ENTRY,
"slap_sasl2dn: Converted SASL name to %s\n",
sasldn->bv_len ? sasldn->bv_val : "<nothing>", 0, 0 );
#else
Debug( LDAP_DEBUG_TRACE, "<==slap_sasl2dn: Converted SASL name to %s\n",
sasldn->bv_len ? sasldn->bv_val : "<nothing>", 0, 0 );
#endif
return;
}
/* Check if a bind can SASL authorize to another identity.
@ -664,7 +657,6 @@ int slap_sasl_authorized( Connection *conn,
{
int rc = LDAP_INAPPROPRIATE_AUTH;
#ifdef HAVE_CYRUS_SASL
/* User binding as anonymous */
if ( authzDN == NULL ) {
rc = LDAP_SUCCESS;
@ -677,7 +669,8 @@ int slap_sasl_authorized( Connection *conn,
authcDN->bv_val, authzDN->bv_val, 0 );
#else
Debug( LDAP_DEBUG_TRACE,
"==>slap_sasl_authorized: can %s become %s?\n", authcDN->bv_val, authzDN->bv_val, 0 );
"==>slap_sasl_authorized: can %s become %s?\n",
authcDN->bv_val, authzDN->bv_val, 0 );
#endif
/* If person is authorizing to self, succeed */
@ -707,7 +700,6 @@ int slap_sasl_authorized( Connection *conn,
rc = LDAP_INAPPROPRIATE_AUTH;
DONE:
#endif
#ifdef NEW_LOGGING
LDAP_LOG( TRANSPORT, RESULTS, "slap_sasl_authorized: return %d\n", rc,0,0 );

View File

@ -169,6 +169,11 @@ typedef struct slap_ssf_set {
slap_ssf_t sss_simple_bind;
} slap_ssf_set_t;
/* Flags for telling slap_sasl_getdn() what type of identity is being passed */
#define SLAP_GETDN_AUTHCID 2
#define SLAP_GETDN_AUTHZID 4
/*
* Index types
*/
@ -1631,6 +1636,8 @@ typedef struct slap_op {
ber_tag_t o_tag; /* tag of the request */
time_t o_time; /* time op was initiated */
char * o_extendedop; /* extended operation OID */
ldap_pvt_thread_t o_tid; /* thread handling this op */
volatile sig_atomic_t o_abandon; /* abandon flag */
@ -1642,6 +1649,7 @@ typedef struct slap_op {
#define SLAP_CRITICAL_CONTROL 2
char o_managedsait;
char o_noop;
char o_proxy_authz;
char o_subentries;
char o_subentries_visibility;
char o_valuesreturnfilter;

View File

@ -270,3 +270,15 @@ slap_modrdn2mods(
return 0;
}
int slap_sasl_getdn( Connection *conn, char *id, int len,
char *user_realm, struct berval *dn, int flags )
{
return -1;
}
int slap_sasl_authorized( Connection *conn,
struct berval *authcDN, struct berval *authzDN )
{
return -1;
}

View File

@ -66,6 +66,39 @@ if test $RC != 0 ; then
exit $RC
fi
echo "Testing ldapwhoami as ${MANAGERDN} for anonymous..."
$LDAPWHOAMI -h $LOCALHOST -p $PORT -D "$MANAGERDN" -w $PASSWD \
-e \!authzid=""
RC=$?
if test $RC != 0 ; then
echo "ldapwhoami failed ($RC)!"
kill -HUP $PID
exit $RC
fi
echo "Testing ldapwhoami as ${MANAGERDN} for dn:$BABSDN..."
$LDAPWHOAMI -h $LOCALHOST -p $PORT -D "$MANAGERDN" -w $PASSWD \
-e \!authzid="dn:$BABSDN"
RC=$?
if test $RC != 0 ; then
echo "ldapwhoami failed ($RC)!"
# kill -HUP $PID
# exit $RC
fi
echo "Testing ldapwhoami as ${MANAGERDN} for u:ursula..."
$LDAPWHOAMI -h $LOCALHOST -p $PORT -D "$MANAGERDN" -w $PASSWD \
-e \!authzid="u:ursula"
RC=$?
if test $RC != 0 ; then
echo "ldapwhoami failed ($RC)!"
# kill -HUP $PID
# exit $RC
fi
kill -HUP $PID
echo ">>>>> Test succeeded"