static int do_check( Connection *c, Operation *op, struct berval *id ) { struct berval authcdn; int rc; rc = slap_sasl_getdn( c, op, id, realm, &authcdn, SLAP_GETDN_AUTHCID ); if ( rc != LDAP_SUCCESS ) { fprintf( stderr, "ID: <%s> check failed %d (%s)\n", id->bv_val, rc, ldap_err2string( rc ) ); rc = 1; } else { if ( !BER_BVISNULL( &authzID ) ) { rc = slap_sasl_authorized( op, &authcdn, &authzID ); fprintf( stderr, "ID: <%s>\n" "authcDN: <%s>\n" "authzDN: <%s>\n" "authorization %s\n", id->bv_val, authcdn.bv_val, authzID.bv_val, rc == LDAP_SUCCESS ? "OK" : "failed" ); } else { fprintf( stderr, "ID: <%s> check succeeded\n" "authcID: <%s>\n", id->bv_val, authcdn.bv_val ); op->o_tmpfree( authcdn.bv_val, op->o_tmpmemctx ); } rc = 0; } return rc; }
int slapauth( int argc, char **argv ) { int rc = EXIT_SUCCESS; const char *progname = "slapauth"; Connection conn = {0}; OperationBuffer opbuf; Operation *op; slap_tool_init( progname, SLAPAUTH, argc, argv ); argv = &argv[ optind ]; argc -= optind; connection_fake_init( &conn, &opbuf, &conn ); op = &opbuf.ob_op; conn.c_sasl_bind_mech = mech; if ( !BER_BVISNULL( &authzID ) ) { struct berval authzdn; rc = slap_sasl_getdn( &conn, op, &authzID, NULL, &authzdn, SLAP_GETDN_AUTHZID ); if ( rc != LDAP_SUCCESS ) { fprintf( stderr, "authzID: <%s> check failed %d (%s)\n", authzID.bv_val, rc, ldap_err2string( rc ) ); rc = 1; BER_BVZERO( &authzID ); goto destroy; } authzID = authzdn; } if ( !BER_BVISNULL( &authcID ) ) { if ( !BER_BVISNULL( &authzID ) || argc == 0 ) { rc = do_check( &conn, op, &authcID ); goto destroy; } for ( ; argc--; argv++ ) { struct berval authzdn; ber_str2bv( argv[ 0 ], 0, 0, &authzID ); rc = slap_sasl_getdn( &conn, op, &authzID, NULL, &authzdn, SLAP_GETDN_AUTHZID ); if ( rc != LDAP_SUCCESS ) { fprintf( stderr, "authzID: <%s> check failed %d (%s)\n", authzID.bv_val, rc, ldap_err2string( rc ) ); rc = -1; BER_BVZERO( &authzID ); if ( !continuemode ) { goto destroy; } } authzID = authzdn; rc = do_check( &conn, op, &authcID ); op->o_tmpfree( authzID.bv_val, op->o_tmpmemctx ); BER_BVZERO( &authzID ); if ( rc && !continuemode ) { goto destroy; } } goto destroy; } for ( ; argc--; argv++ ) { struct berval id; ber_str2bv( argv[ 0 ], 0, 0, &id ); rc = do_check( &conn, op, &id ); if ( rc && !continuemode ) { goto destroy; } } destroy: ; if ( !BER_BVISNULL( &authzID ) ) { op->o_tmpfree( authzID.bv_val, op->o_tmpmemctx ); } if ( slap_tool_destroy()) rc = EXIT_FAILURE; return rc; }
/* Convert a SASL authcid or authzid into a DN. Store the DN in an * auxiliary property, so that we can refer to it in sasl_authorize * without interfering with anything else. Also, the SASL username * buffer is constrained to 256 characters, and our DNs could be * much longer (SLAP_LDAPDN_MAXLEN, currently set to 8192) */ static int slap_sasl_canonicalize( sasl_conn_t *sconn, void *context, const char *in, unsigned inlen, unsigned flags, const char *user_realm, char *out, unsigned out_max, unsigned *out_len) { Connection *conn = (Connection *)context; struct propctx *props = sasl_auxprop_getctx( sconn ); struct propval auxvals[ SLAP_SASL_PROP_COUNT ] = { { 0 } }; struct berval dn; int rc, which; const char *names[2]; struct berval bvin; *out_len = 0; Debug( LDAP_DEBUG_ARGS, "SASL Canonicalize [conn=%ld]: %s=\"%s\"\n", conn ? (long) conn->c_connid : -1L, (flags & SASL_CU_AUTHID) ? "authcid" : "authzid", in ? in : "<empty>"); /* If name is too big, just truncate. We don't care, we're * using DNs, not the usernames. */ if ( inlen > out_max ) inlen = out_max-1; /* This is a Simple Bind using SPASSWD. That means the in-directory * userPassword of the Binding user already points at SASL, so it * cannot be used to actually satisfy a password comparison. Just * ignore it, some other mech will process it. */ if ( !conn->c_sasl_bindop || conn->c_sasl_bindop->orb_method != LDAP_AUTH_SASL ) goto done; /* See if we need to add request, can only do it once */ prop_getnames( props, slap_propnames, auxvals ); if ( !auxvals[0].name ) prop_request( props, slap_propnames ); if ( flags & SASL_CU_AUTHID ) which = SLAP_SASL_PROP_AUTHCLEN; else which = SLAP_SASL_PROP_AUTHZLEN; /* Need to store the Connection for auxprop_lookup */ if ( !auxvals[SLAP_SASL_PROP_CONN].values ) { names[0] = slap_propnames[SLAP_SASL_PROP_CONN]; names[1] = NULL; prop_set( props, names[0], (char *)&conn, sizeof( conn ) ); } /* Already been here? */ if ( auxvals[which].values ) goto done; /* Normally we require an authzID to have a u: or dn: prefix. * However, SASL frequently gives us an authzID that is just * an exact copy of the authcID, without a prefix. We need to * detect and allow this condition. If SASL calls canonicalize * with SASL_CU_AUTHID|SASL_CU_AUTHZID this is a no-brainer. * But if it's broken into two calls, we need to remember the * authcID so that we can compare the authzID later. We store * the authcID temporarily in conn->c_sasl_dn. We necessarily * finish Canonicalizing before Authorizing, so there is no * conflict with slap_sasl_authorize's use of this temp var. * * The SASL EXTERNAL mech is backwards from all the other mechs, * it does authzID before the authcID. If we see that authzID * has already been done, don't do anything special with authcID. */ if ( flags == SASL_CU_AUTHID && !auxvals[SLAP_SASL_PROP_AUTHZ].values ) { conn->c_sasl_dn.bv_val = (char *) in; conn->c_sasl_dn.bv_len = 0; } else if ( flags == SASL_CU_AUTHZID && conn->c_sasl_dn.bv_val ) { rc = strcmp( in, conn->c_sasl_dn.bv_val ); conn->c_sasl_dn.bv_val = NULL; /* They were equal, no work needed */ if ( !rc ) goto done; } bvin.bv_val = (char *)in; bvin.bv_len = inlen; rc = slap_sasl_getdn( conn, NULL, &bvin, (char *)user_realm, &dn, (flags & SASL_CU_AUTHID) ? SLAP_GETDN_AUTHCID : SLAP_GETDN_AUTHZID ); if ( rc != LDAP_SUCCESS ) { sasl_seterror( sconn, 0, ldap_err2string( rc ) ); return SASL_NOAUTHZ; } names[0] = slap_propnames[which]; names[1] = NULL; prop_set( props, names[0], (char *)&dn.bv_len, sizeof( dn.bv_len ) ); which++; names[0] = slap_propnames[which]; prop_set( props, names[0], dn.bv_val, dn.bv_len ); Debug( LDAP_DEBUG_ARGS, "SASL Canonicalize [conn=%ld]: %s=\"%s\"\n", conn ? (long) conn->c_connid : -1L, names[0]+1, dn.bv_val ? dn.bv_val : "<EMPTY>" ); /* Not needed any more, SASL has copied it */ if ( conn && conn->c_sasl_bindop ) conn->c_sasl_bindop->o_tmpfree( dn.bv_val, conn->c_sasl_bindop->o_tmpmemctx ); done: AC_MEMCPY( out, in, inlen ); out[inlen] = '\0'; *out_len = inlen; return SASL_OK; }