static int ldap_back_exop_passwd( Operation *op, SlapReply *rs, ldapconn_t **lcp ) { ldapinfo_t *li = (ldapinfo_t *) op->o_bd->be_private; ldapconn_t *lc = *lcp; req_pwdexop_s *qpw = &op->oq_pwdexop; LDAPMessage *res; ber_int_t msgid; int rc, isproxy, freedn = 0; int do_retry = 1; char *text = NULL; struct berval dn = op->o_req_dn, ndn = op->o_req_ndn; assert( lc != NULL ); assert( rs->sr_ctrls == NULL ); if ( BER_BVISNULL( &ndn ) && op->ore_reqdata != NULL ) { /* NOTE: most of this code is mutated * from slap_passwd_parse(); * But here we only need * the first berval... */ ber_tag_t tag; ber_len_t len = -1; BerElementBuffer berbuf; BerElement *ber = (BerElement *)&berbuf; struct berval tmpid = BER_BVNULL; if ( op->ore_reqdata->bv_len == 0 ) { return LDAP_PROTOCOL_ERROR; } /* ber_init2 uses reqdata directly, doesn't allocate new buffers */ ber_init2( ber, op->ore_reqdata, 0 ); tag = ber_scanf( ber, "{" /*}*/ ); if ( tag == LBER_ERROR ) { return LDAP_PROTOCOL_ERROR; } tag = ber_peek_tag( ber, &len ); if ( tag == LDAP_TAG_EXOP_MODIFY_PASSWD_ID ) { tag = ber_get_stringbv( ber, &tmpid, LBER_BV_NOTERM ); if ( tag == LBER_ERROR ) { return LDAP_PROTOCOL_ERROR; } } if ( !BER_BVISEMPTY( &tmpid ) ) { char idNull = tmpid.bv_val[tmpid.bv_len]; tmpid.bv_val[tmpid.bv_len] = '\0'; rs->sr_err = dnPrettyNormal( NULL, &tmpid, &dn, &ndn, op->o_tmpmemctx ); tmpid.bv_val[tmpid.bv_len] = idNull; if ( rs->sr_err != LDAP_SUCCESS ) { /* should have been successfully parsed earlier! */ return rs->sr_err; } freedn = 1; } else { dn = op->o_dn; ndn = op->o_ndn; } } isproxy = ber_bvcmp( &ndn, &op->o_ndn ); Debug( LDAP_DEBUG_ARGS, "==> ldap_back_exop_passwd(\"%s\")%s\n", dn.bv_val, isproxy ? " (proxy)" : "", 0 ); retry: rc = ldap_passwd( lc->lc_ld, &dn, qpw->rs_old.bv_val ? &qpw->rs_old : NULL, qpw->rs_new.bv_val ? &qpw->rs_new : NULL, op->o_ctrls, NULL, &msgid ); if ( rc == LDAP_SUCCESS ) { /* TODO: set timeout? */ /* by now, make sure no timeout is used (ITS#6282) */ struct timeval tv = { -1, 0 }; if ( ldap_result( lc->lc_ld, msgid, LDAP_MSG_ALL, &tv, &res ) == -1 ) { ldap_get_option( lc->lc_ld, LDAP_OPT_ERROR_NUMBER, &rc ); rs->sr_err = rc; } else { /* only touch when activity actually took place... */ if ( li->li_idle_timeout ) { lc->lc_time = op->o_time; } /* sigh. parse twice, because parse_passwd * doesn't give us the err / match / msg info. */ rc = ldap_parse_result( lc->lc_ld, res, &rs->sr_err, (char **)&rs->sr_matched, &text, NULL, &rs->sr_ctrls, 0 ); if ( rc == LDAP_SUCCESS ) { if ( rs->sr_err == LDAP_SUCCESS ) { struct berval newpw; /* this never happens because * the frontend is generating * the new password, so when * the passwd exop is proxied, * it never delegates password * generation to the remote server */ rc = ldap_parse_passwd( lc->lc_ld, res, &newpw ); if ( rc == LDAP_SUCCESS && !BER_BVISNULL( &newpw ) ) { rs->sr_type = REP_EXTENDED; rs->sr_rspdata = slap_passwd_return( &newpw ); free( newpw.bv_val ); } } else { rc = rs->sr_err; } } ldap_msgfree( res ); } } if ( rc != LDAP_SUCCESS ) { rs->sr_err = slap_map_api2result( rs ); if ( rs->sr_err == LDAP_UNAVAILABLE && do_retry ) { do_retry = 0; if ( ldap_back_retry( &lc, op, rs, LDAP_BACK_SENDERR ) ) { goto retry; } } if ( LDAP_BACK_QUARANTINE( li ) ) { ldap_back_quarantine( op, rs ); } if ( text ) rs->sr_text = text; send_ldap_extended( op, rs ); /* otherwise frontend resends result */ rc = rs->sr_err = SLAPD_ABANDON; } else if ( LDAP_BACK_QUARANTINE( li ) ) { ldap_back_quarantine( op, rs ); } ldap_pvt_thread_mutex_lock( &li->li_counter_mutex ); ldap_pvt_mp_add( li->li_ops_completed[ SLAP_OP_EXTENDED ], 1 ); ldap_pvt_thread_mutex_unlock( &li->li_counter_mutex ); if ( freedn ) { op->o_tmpfree( dn.bv_val, op->o_tmpmemctx ); op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx ); } /* these have to be freed anyway... */ if ( rs->sr_matched ) { free( (char *)rs->sr_matched ); rs->sr_matched = NULL; } if ( rs->sr_ctrls ) { ldap_controls_free( rs->sr_ctrls ); rs->sr_ctrls = NULL; } if ( text ) { free( text ); rs->sr_text = NULL; } /* in case, cleanup handler */ if ( lc == NULL ) { *lcp = NULL; } return rc; }
/* NOTE: The DN in *id is NOT NUL-terminated here. dnNormalize will * reject it in this condition, the caller must NUL-terminate it. * FIXME: should dnNormalize still be complaining about that? */ int slap_passwd_parse( struct berval *reqdata, struct berval *id, struct berval *oldpass, struct berval *newpass, const char **text ) { int rc = LDAP_SUCCESS; ber_tag_t tag; ber_len_t len = -1; BerElementBuffer berbuf; BerElement *ber = (BerElement *)&berbuf; if( reqdata == NULL ) { return LDAP_SUCCESS; } if( reqdata->bv_len == 0 ) { *text = "empty request data field"; return LDAP_PROTOCOL_ERROR; } /* ber_init2 uses reqdata directly, doesn't allocate new buffers */ ber_init2( ber, reqdata, 0 ); tag = ber_skip_tag( ber, &len ); if( tag != LBER_SEQUENCE ) { Debug( LDAP_DEBUG_TRACE, "slap_passwd_parse: decoding error\n", 0, 0, 0 ); rc = LDAP_PROTOCOL_ERROR; goto done; } tag = ber_peek_tag( ber, &len ); if( tag == LDAP_TAG_EXOP_MODIFY_PASSWD_ID ) { if( id == NULL ) { Debug( LDAP_DEBUG_TRACE, "slap_passwd_parse: ID not allowed.\n", 0, 0, 0 ); *text = "user must change own password"; rc = LDAP_UNWILLING_TO_PERFORM; goto done; } tag = ber_get_stringbv( ber, id, LBER_BV_NOTERM ); if( tag == LBER_ERROR ) { Debug( LDAP_DEBUG_TRACE, "slap_passwd_parse: ID parse failed.\n", 0, 0, 0 ); goto decoding_error; } tag = ber_peek_tag( ber, &len ); } if( tag == LDAP_TAG_EXOP_MODIFY_PASSWD_OLD ) { if( oldpass == NULL ) { Debug( LDAP_DEBUG_TRACE, "slap_passwd_parse: OLD not allowed.\n", 0, 0, 0 ); *text = "use bind to verify old password"; rc = LDAP_UNWILLING_TO_PERFORM; goto done; } tag = ber_get_stringbv( ber, oldpass, LBER_BV_NOTERM ); if( tag == LBER_ERROR ) { Debug( LDAP_DEBUG_TRACE, "slap_passwd_parse: OLD parse failed.\n", 0, 0, 0 ); goto decoding_error; } if( oldpass->bv_len == 0 ) { Debug( LDAP_DEBUG_TRACE, "slap_passwd_parse: OLD empty.\n", 0, 0, 0 ); *text = "old password value is empty"; rc = LDAP_UNWILLING_TO_PERFORM; goto done; } tag = ber_peek_tag( ber, &len ); } if( tag == LDAP_TAG_EXOP_MODIFY_PASSWD_NEW ) { if( newpass == NULL ) { Debug( LDAP_DEBUG_TRACE, "slap_passwd_parse: NEW not allowed.\n", 0, 0, 0 ); *text = "user specified passwords disallowed"; rc = LDAP_UNWILLING_TO_PERFORM; goto done; } tag = ber_get_stringbv( ber, newpass, LBER_BV_NOTERM ); if( tag == LBER_ERROR ) { Debug( LDAP_DEBUG_TRACE, "slap_passwd_parse: NEW parse failed.\n", 0, 0, 0 ); goto decoding_error; } if( newpass->bv_len == 0 ) { Debug( LDAP_DEBUG_TRACE, "slap_passwd_parse: NEW empty.\n", 0, 0, 0 ); *text = "new password value is empty"; rc = LDAP_UNWILLING_TO_PERFORM; goto done; } tag = ber_peek_tag( ber, &len ); } if( len != 0 ) { decoding_error: Debug( LDAP_DEBUG_TRACE, "slap_passwd_parse: decoding error, len=%ld\n", (long) len, 0, 0 ); *text = "data decoding error"; rc = LDAP_PROTOCOL_ERROR; } done: return rc; }
/* Convert a structured DN from an X.509 certificate into an LDAPV3 DN. * x509_name must be raw DER. If func is non-NULL, the * constructed DN will use numeric OIDs to identify attributeTypes, * and the func() will be invoked to rewrite the DN with the given * flags. * * Otherwise the DN will use shortNames from a hardcoded table. */ int ldap_X509dn2bv( void *x509_name, struct berval *bv, LDAPDN_rewrite_func *func, unsigned flags ) { LDAPDN newDN; LDAPRDN newRDN; LDAPAVA *newAVA, *baseAVA; BerElementBuffer berbuf; BerElement *ber = (BerElement *)&berbuf; char oids[8192], *oidptr = oids, *oidbuf = NULL; void *ptrs[2048]; char *dn_end, *rdn_end; int i, navas, nrdns, rc = LDAP_SUCCESS; size_t dnsize, oidrem = sizeof(oids), oidsize = 0; int csize; ber_tag_t tag; ber_len_t len; oid_name *oidname; struct berval Oid, Val, oid2, *in = x509_name; assert( bv != NULL ); bv->bv_len = 0; bv->bv_val = NULL; navas = 0; nrdns = 0; /* A DN is a SEQUENCE of RDNs. An RDN is a SET of AVAs. * An AVA is a SEQUENCE of attr and value. * Count the number of AVAs and RDNs */ ber_init2( ber, in, LBER_USE_DER ); tag = ber_peek_tag( ber, &len ); if ( tag != LBER_SEQUENCE ) return LDAP_DECODING_ERROR; for ( tag = ber_first_element( ber, &len, &dn_end ); tag == LBER_SET; tag = ber_next_element( ber, &len, dn_end )) { nrdns++; for ( tag = ber_first_element( ber, &len, &rdn_end ); tag == LBER_SEQUENCE; tag = ber_next_element( ber, &len, rdn_end )) { tag = ber_skip_tag( ber, &len ); ber_skip_data( ber, len ); navas++; } } /* Allocate the DN/RDN/AVA stuff as a single block */ dnsize = sizeof(LDAPRDN) * (nrdns+1); dnsize += sizeof(LDAPAVA *) * (navas+nrdns); dnsize += sizeof(LDAPAVA) * navas; if (dnsize > sizeof(ptrs)) { newDN = (LDAPDN)LDAP_MALLOC( dnsize ); if ( newDN == NULL ) return LDAP_NO_MEMORY; } else { newDN = (LDAPDN)(char *)ptrs; } newDN[nrdns] = NULL; newRDN = (LDAPRDN)(newDN + nrdns+1); newAVA = (LDAPAVA *)(newRDN + navas + nrdns); baseAVA = newAVA; /* Rewind and start extracting */ ber_rewind( ber ); tag = ber_first_element( ber, &len, &dn_end ); for ( i = nrdns - 1; i >= 0; i-- ) { newDN[i] = newRDN; for ( tag = ber_first_element( ber, &len, &rdn_end ); tag == LBER_SEQUENCE; tag = ber_next_element( ber, &len, rdn_end )) { *newRDN++ = newAVA; tag = ber_skip_tag( ber, &len ); tag = ber_get_stringbv( ber, &Oid, LBER_BV_NOTERM ); if ( tag != LBER_TAG_OID ) { rc = LDAP_DECODING_ERROR; goto nomem; } oid2.bv_val = oidptr; oid2.bv_len = oidrem; if ( ber_decode_oid( &Oid, &oid2 ) < 0 ) { rc = LDAP_DECODING_ERROR; goto nomem; } oidname = find_oid( &oid2 ); if ( !oidname ) { newAVA->la_attr = oid2; oidptr += oid2.bv_len + 1; oidrem -= oid2.bv_len + 1; /* Running out of OID buffer space? */ if (oidrem < 128) { if ( oidsize == 0 ) { oidsize = sizeof(oids) * 2; oidrem = oidsize; oidbuf = LDAP_MALLOC( oidsize ); if ( oidbuf == NULL ) goto nomem; oidptr = oidbuf; } else { char *old = oidbuf; oidbuf = LDAP_REALLOC( oidbuf, oidsize*2 ); if ( oidbuf == NULL ) goto nomem; /* Buffer moved! Fix AVA pointers */ if ( old != oidbuf ) { LDAPAVA *a; long dif = oidbuf - old; for (a=baseAVA; a<=newAVA; a++){ if (a->la_attr.bv_val >= old && a->la_attr.bv_val <= (old + oidsize)) a->la_attr.bv_val += dif; } } oidptr = oidbuf + oidsize - oidrem; oidrem += oidsize; oidsize *= 2; } } } else { if ( func ) { newAVA->la_attr = oidname->oid; } else { newAVA->la_attr = oidname->name; } } newAVA->la_private = NULL; newAVA->la_flags = LDAP_AVA_STRING; tag = ber_get_stringbv( ber, &Val, LBER_BV_NOTERM ); switch(tag) { case LBER_TAG_UNIVERSAL: /* This uses 32-bit ISO 10646-1 */ csize = 4; goto to_utf8; case LBER_TAG_BMP: /* This uses 16-bit ISO 10646-1 */ csize = 2; goto to_utf8; case LBER_TAG_TELETEX: /* This uses 8-bit, assume ISO 8859-1 */ csize = 1; to_utf8: rc = ldap_ucs_to_utf8s( &Val, csize, &newAVA->la_value ); newAVA->la_flags |= LDAP_AVA_NONPRINTABLE; allocd: newAVA->la_flags |= LDAP_AVA_FREE_VALUE; if (rc != LDAP_SUCCESS) goto nomem; break; case LBER_TAG_UTF8: newAVA->la_flags |= LDAP_AVA_NONPRINTABLE; /* This is already in UTF-8 encoding */ case LBER_TAG_IA5: case LBER_TAG_PRINTABLE: /* These are always 7-bit strings */ newAVA->la_value = Val; break; case LBER_BITSTRING: /* X.690 bitString value converted to RFC4517 Bit String */ rc = der_to_ldap_BitString( &Val, &newAVA->la_value ); goto allocd; default: /* Not a string type at all */ newAVA->la_flags = 0; newAVA->la_value = Val; break; } newAVA++; } *newRDN++ = NULL; tag = ber_next_element( ber, &len, dn_end ); } if ( func ) { rc = func( newDN, flags, NULL ); if ( rc != LDAP_SUCCESS ) goto nomem; } rc = ldap_dn2bv_x( newDN, bv, LDAP_DN_FORMAT_LDAPV3, NULL ); nomem: for (;baseAVA < newAVA; baseAVA++) { if (baseAVA->la_flags & LDAP_AVA_FREE_ATTR) LDAP_FREE( baseAVA->la_attr.bv_val ); if (baseAVA->la_flags & LDAP_AVA_FREE_VALUE) LDAP_FREE( baseAVA->la_value.bv_val ); } if ( oidsize != 0 ) LDAP_FREE( oidbuf ); if ( newDN != (LDAPDN)(char *) ptrs ) LDAP_FREE( newDN ); return rc; }