netscape-revival
security/lib/nav/certldap.c
/*
* support for fetching certs from ldap
*
* Copyright © 1995 Netscape Communications Corporation, all rights reserved.
*
* $Id: certldap.c,v 1.3.2.1 1997/05/24 00:23:39 jwz Exp $
*/
#include "xp.h"
#include "cert.h"
#include "net.h"
#include "dirprefs.h"
#include "ldap.h"
#include "secnav.h"
#include "certldap.h"
#include "secitem.h"
#include "xpgetstr.h"
extern int MK_OUT_OF_MEMORY;
extern int XP_DIRECTORY_PASSWORD;
CertLdapConnData *
SECNAV_CertLdapLoad(URL_Struct *urlStr)
{
CertLdapConnData *connData = NULL;
char *url;
LDAP *ld;
LBER_SOCKET socket;
int ret;
CertLdapOpDesc *op;
op = (CertLdapOpDesc *)urlStr->sec_private;
/* it is a bogus caller if sec_private is NULL */
if ( op == NULL ) {
return(NULL);
}
/* allocate connection data */
connData = PORT_ArenaZAlloc(op->arena, sizeof(CertLdapConnData));
if ( connData == NULL ) {
PORT_SetError(MK_OUT_OF_MEMORY);
goto loser;
}
connData->urlStr = urlStr;
connData->op = op;
op->connData = connData;
connData->ld = ldap_init(op->servername, op->serverport);
if ( connData->ld == NULL ) {
goto loser;
}
ret = DIR_SetupSecureConnection(connData->ld);
if ( ! op->isSecure ) {
ldap_set_option (connData->ld, LDAP_OPT_SSL, LDAP_OPT_OFF);
}
/* lets try anonymous first */
connData->msgid = ldap_simple_bind(connData->ld, NULL, NULL);
if ( connData->msgid == -1 ) {
goto loser;
}
ret = ldap_get_option(connData->ld, LDAP_OPT_DESC, (void *)&socket);
if ( ret != LDAP_SUCCESS ) {
goto loser;
}
connData->fd = (unsigned long)socket;
connData->state = certLdapBindWait;
done:
return(connData);
loser:
if ( op->cb != NULL ) {
if ( connData != NULL ) {
connData->state = certLdapError;
}
op->rv = SECFailure;
(* op->cb)(op);
}
return(NULL);
}
static char *put_cert_attr[] = {
NULL, NULL
};
static char *get_cert_attr[] = {
NULL, ATTR_USER_CERT, ATTR_SMIME_CERT, NULL
};
static char *
buildFilter(CertLdapOpDesc *op)
{
int i;
char *filter;
PORT_Assert(op->searchType == certLdapSearchMail);
PORT_Assert(op->numnames > 0);
if ( op->numnames == 1 ) {
filter = PR_smprintf("(%s=%s)", op->mailAttrName, op->names[0]);
} else {
filter = PR_smprintf("(|(%s=%s)", op->mailAttrName, op->names[0]);
if ( filter == NULL ) {
return(NULL);
}
for ( i = 1; i < op->numnames; i++ ) {
filter = PR_sprintf_append(filter, "(%s=%s)", op->mailAttrName,
op->names[i]);
if ( filter == NULL ) {
return(NULL);
}
}
filter = PR_sprintf_append(filter, ")");
}
if ( filter == NULL ) {
return(NULL);
}
return(filter);
}
static SECStatus
doPost(CertLdapConnData *connData)
{
CertLdapOpDesc *op;
SECStatus rv;
LDAPMod certMod;
struct berval certval;
struct berval *certvals[2];
LDAPMod *mods[2];
op = connData->op;
certMod.mod_type = ATTR_SMIME_CERT;
certMod.mod_op = LDAP_MOD_REPLACE | LDAP_MOD_BVALUES;
certval.bv_val = (char *)op->postData.data;
certval.bv_len = op->postData.len;
certvals[0] = &certval;
certvals[1] = NULL;
certMod.mod_bvalues = certvals;
mods[0] = &certMod;
mods[1] = NULL;
connData->msgid = ldap_modify(connData->ld, connData->postdn, mods);
if ( connData->msgid == -1 ) {
return(SECFailure);
}
return(SECSuccess);
}
static SECStatus
doSearch(CertLdapConnData *connData)
{
char *filter;
CertLdapOpDesc *op;
SECStatus rv;
char **attr;
op = connData->op;
/* search for the entry */
filter = buildFilter(op);
if ( filter == NULL ) {
goto loser;
}
if ( op->type == certLdapPostCert ) {
PORT_Assert(op->numnames == 1);
attr = put_cert_attr;
} else {
attr = get_cert_attr;
}
attr[0] = op->mailAttrName;
connData->msgid = ldap_search(connData->ld, op->base,
LDAP_SCOPE_SUBTREE, filter, attr, 0);
PORT_Free(filter);
if ( connData->msgid == -1 ) {
goto loser;
}
return(SECSuccess);
loser:
return(SECFailure);
}
static SECStatus
doPasswordBind(CertLdapConnData *connData)
{
CertLdapOpDesc *op;
SECStatus rv;
char *pw;
op = connData->op;
pw = FE_PromptPassword(((MWContext *)op->window), XP_GetString(XP_DIRECTORY_PASSWORD));
if ( pw == NULL ) {
return(SECFailure);
}
/* lets try anonymous first */
connData->msgid = ldap_simple_bind(connData->ld, connData->postdn, pw);
SECNAV_ZeroPassword(pw);
PORT_Free(pw);
if ( connData->msgid == -1 ) {
return(SECFailure);
}
return(SECSuccess);
}
int
SECNAV_CertLdapProcess(CertLdapConnData *connData)
{
int ret;
LDAPMessage *result;
LDAPMessage *entry;
struct timeval zerotime;
struct berval **listOfCerts;
SECItem derCert;
char *dn;
char **vals;
CertLdapOpDesc *op;
int i;
SECStatus rv;
int err;
op = connData->op;
zerotime.tv_sec = zerotime.tv_usec = 0L;
while ( ( ret = ldap_result(connData->ld, connData->msgid,
0, &zerotime, &result) ) > 0 ) {
if ( ret != LDAP_RES_SEARCH_ENTRY ) {
/* ldap_result2error() doesn't work right if the result
* is not the final one from a search
*/
if ( ( err = ldap_result2error(connData->ld, result, 0) )
!= LDAP_SUCCESS ) {
if ( ( ( connData->state == certLdapPostWait ) ||
( connData->state == certLdapPWBindWait ) ) &&
( ( err == LDAP_INSUFFICIENT_ACCESS ) ||
( err == LDAP_INVALID_CREDENTIALS ) ) ) {
/* need to re-bind with password to write to
* the directory
*/
rv = doPasswordBind(connData);
if ( rv != SECSuccess ) {
goto loser;
}
connData->state = certLdapPWBindWait;
continue;
} else {
goto loser;
}
}
}
switch (connData->state) {
case certLdapPWBindWait:
PORT_Assert(ret == LDAP_RES_BIND);
PORT_Assert(op->type == certLdapPostCert);
rv = doPost(connData);
if ( rv != SECSuccess ) {
goto loser;
}
connData->state = certLdapPostWait;
break;
case certLdapBindWait:
PORT_Assert(ret == LDAP_RES_BIND);
rv = doSearch(connData);
if ( rv != SECSuccess ) {
goto loser;
}
connData->state = certLdapSearchWait;
break;
case certLdapSearchWait:
PORT_Assert( ( ret == LDAP_RES_SEARCH_RESULT ) ||
( ret == LDAP_RES_SEARCH_ENTRY ) );
if ( op->type == certLdapPostCert ) {
if ( connData->postdn == NULL ) {
/* get DN from result, and then post the new cert */
entry = ldap_first_entry(connData->ld, result);
if ( entry == NULL ) {
goto loser;
}
dn = ldap_get_dn(connData->ld, entry);
if ( dn == NULL ) {
goto loser;
}
connData->postdn = PORT_ArenaStrdup(op->arena, dn);
if ( connData->postdn == NULL ) {
goto loser;
}
}
if ( ret == LDAP_RES_SEARCH_RESULT ) {
/* wait til we get the last search result before posting*/
rv = doPost(connData);
if ( rv != SECSuccess ) {
goto loser;
}
connData->state = certLdapPostWait;
}
} else {
/* collect the certs from the results */
entry = ldap_first_entry(connData->ld, result);
while ( entry ) {
listOfCerts = ldap_get_values_len(connData->ld, entry,
ATTR_SMIME_CERT);
if ( listOfCerts == NULL ) {
listOfCerts = ldap_get_values_len(connData->ld, entry,
ATTR_USER_CERT);
if ( listOfCerts == NULL ) {
goto endloop;
}
}
vals = ldap_get_values(connData->ld, entry,
op->mailAttrName);
if ( ( vals[0] == NULL ) || ( listOfCerts[0] == NULL ) ) {
goto endloop;
}
for ( i = 0; i < op->numnames; i++ ) {
/* look for matching email address slot and drop the
* cert into it
*/
if ( PORT_Strcmp(vals[0], op->names[i])== 0){
derCert.len = listOfCerts[0]->bv_len;
derCert.data = (unsigned char *)listOfCerts[0]->bv_val;
rv = SECITEM_CopyItem(op->arena,
&op->rawcerts[i],
&derCert);
break;
}
}
endloop:
entry = ldap_next_entry(connData->ld, entry);
}
if ( ret == LDAP_RES_SEARCH_RESULT ) {
ret = ldap_unbind(connData->ld);
connData->state = certLdapDone;
/* we are done, so set the return value to cause us to
* stop, and call the callback
*/
ret = 1;
if ( op->cb != NULL ) {
op->rv = SECSuccess;
(* op->cb)(op);
}
goto done;
}
}
break;
case certLdapPostWait:
/* post is done */
PORT_Assert(ret == LDAP_RES_MODIFY);
ret = ldap_unbind(connData->ld);
connData->state = certLdapDone;
ret = 1;
if ( op->cb != NULL ) {
op->rv = SECSuccess;
(* op->cb)(op);
}
goto done;
}
}
if ( ret == -1 ) {
goto loser;
}
/* ret == 0 */
/* not ready yet */
goto done;
loser:
connData->state = certLdapError;
if ( op->cb != NULL ) {
op->rv = SECFailure;
(* op->cb)(op);
}
/* try to close down the connection */
SECNAV_CertLdapInterrupt(connData);
return(-1);
done:
return(ret);
}
int
SECNAV_CertLdapInterrupt(CertLdapConnData *connData)
{
int ret;
CertLdapOpDesc *op;
int err = 0;
op = connData->op;
ret = ldap_abandon(connData->ld, connData->msgid);
if ( ret != LDAP_SUCCESS ) {
err = -1;
}
ret = ldap_unbind(connData->ld);
if ( ret != LDAP_SUCCESS ) {
err = -1;
}
return(err);
}