Add very preliminary and quick port of an old scam
code that implements ldap-authentication support, needs to be rewritten for more specific OpenSC usage some other day. Work in progress, only tested with FINEID cards. git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@1431 c6295689-39f2-0310-b995-f0e70906c6a9
This commit is contained in:
parent
09f2d373b2
commit
1ea97fbb5e
|
@ -24,13 +24,13 @@
|
|||
#endif
|
||||
#if defined(HAVE_OPENSSL) && defined(HAVE_LDAP)
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <pwd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <openssl/x509.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/rand.h>
|
||||
|
@ -38,20 +38,27 @@
|
|||
#include <opensc/pkcs15.h>
|
||||
#include <opensc/scldap.h>
|
||||
#include <opensc/scrandom.h>
|
||||
#include <opensc/log.h>
|
||||
#include "cert_support.h"
|
||||
#include "scam.h"
|
||||
|
||||
typedef struct _scam_method_data {
|
||||
struct sc_context *ctx;
|
||||
struct sc_card *card;
|
||||
struct sc_pkcs15_card *p15card;
|
||||
scldap_context *lctx;
|
||||
int card_locked;
|
||||
|
||||
struct sc_pkcs15_object *objs[32];
|
||||
struct sc_pkcs15_cert_info *cinfo;
|
||||
struct sc_pkcs15_object *prkey, *pin;
|
||||
|
||||
scldap_context *lctx;
|
||||
char *scldap_entry;
|
||||
} scam_method_data;
|
||||
|
||||
#define MAX_PATHLEN 10
|
||||
#define MAX_ENTRYLEN 256
|
||||
|
||||
const char *p15_ldap_usage(void)
|
||||
{
|
||||
static char buf[500];
|
||||
|
@ -180,6 +187,11 @@ int p15_ldap_init(scam_context * sctx, int argc, const char **argv)
|
|||
return SCAM_FAILED;
|
||||
}
|
||||
scldap_parse_arguments(&data->lctx, argc, argv);
|
||||
data->scldap_entry = (char *) malloc(MAX_ENTRYLEN);
|
||||
if (!data->scldap_entry) {
|
||||
return SCAM_FAILED;
|
||||
}
|
||||
memset(data->scldap_entry, 0, MAX_ENTRYLEN);
|
||||
return SCAM_SUCCESS;
|
||||
}
|
||||
|
||||
|
@ -212,41 +224,211 @@ int p15_ldap_qualify(scam_context * sctx, unsigned char *password)
|
|||
return SCAM_SUCCESS;
|
||||
}
|
||||
|
||||
static int copy_result(scldap_result * lresult, unsigned char **result, unsigned long *resultlen)
|
||||
{
|
||||
if (!lresult)
|
||||
return -1;
|
||||
*result = NULL;
|
||||
*resultlen = 0;
|
||||
*result = (unsigned char *) malloc(lresult->result[0].datalen + 1);
|
||||
if (!*result)
|
||||
return -1;
|
||||
memset(*result, 0, lresult->result[0].datalen + 1);
|
||||
memcpy(*result, lresult->result[0].data, lresult->result[0].datalen);
|
||||
*resultlen = lresult->result[0].datalen;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void modify_base(scam_context * sctx, const char *entry, char *dn)
|
||||
{
|
||||
scam_method_data *data = (scam_method_data *) sctx->method_data;
|
||||
char approx_entry[MAX_ENTRYLEN];
|
||||
int entrynum = -1;
|
||||
|
||||
if (!sctx || !entry || !dn)
|
||||
return;
|
||||
entrynum = scldap_get_entry(data->lctx, entry);
|
||||
if (entrynum < 0) {
|
||||
return;
|
||||
}
|
||||
snprintf(approx_entry, MAX_ENTRYLEN, "%s %s approx base", data->p15card->label, data->p15card->manufacturer_id);
|
||||
if (scldap_approx_base_by_dn(data->lctx, approx_entry, dn, &data->lctx->entry[entrynum].base) < 0) {
|
||||
return;
|
||||
}
|
||||
sc_debug(data->ctx, "modify_base: %s\n", data->lctx->entry[entrynum].base);
|
||||
}
|
||||
|
||||
int p15_ldap_auth(scam_context * sctx, int argc, const char **argv,
|
||||
const char *user, const char *password)
|
||||
{
|
||||
scam_method_data *data = (scam_method_data *) sctx->method_data;
|
||||
struct sc_pkcs15_cert *p15cert = NULL;
|
||||
scCertificate *CardCert = NULL, *Cert = NULL, *CACerts[MAX_PATHLEN];
|
||||
X509 *currcert = NULL;
|
||||
u8 random_data[20], chg[256];
|
||||
int r, err = SCAM_FAILED, chglen;
|
||||
EVP_PKEY *pubkey = NULL;
|
||||
X509 *cert = NULL;
|
||||
unsigned char *ptr = NULL;
|
||||
scldap_result *lresult = NULL;
|
||||
int r = 0, i = 0, err = SCAM_FAILED, chglen;
|
||||
|
||||
if (!sctx->method_data) {
|
||||
return SCAM_FAILED;
|
||||
}
|
||||
for (i = 0; i < MAX_PATHLEN; i++) {
|
||||
CACerts[i] = NULL;
|
||||
}
|
||||
Cert = certAlloc();
|
||||
if (!Cert) {
|
||||
goto end;
|
||||
}
|
||||
CardCert = certAlloc();
|
||||
if (!CardCert) {
|
||||
goto end;
|
||||
}
|
||||
r = sc_pkcs15_read_certificate(data->p15card, data->cinfo, &p15cert);
|
||||
if (r != SC_SUCCESS) {
|
||||
scam_print_msg(sctx, "sc_pkcs15_read_certificate: %s\n", sc_strerror(r));
|
||||
goto end;
|
||||
}
|
||||
cert = X509_new();
|
||||
ptr = p15cert->data;
|
||||
if (!d2i_X509(&cert, &ptr, p15cert->data_len)) {
|
||||
scam_log_msg(sctx, "Invalid certificate. (user %s)\n", user);
|
||||
|
||||
/* FIXME */
|
||||
CardCert->len = p15cert->data_len;
|
||||
CardCert->buf = (unsigned char *) malloc(CardCert->len);
|
||||
if (!CardCert->buf) {
|
||||
scam_print_msg(sctx, "out of memory\n", sc_strerror(r));
|
||||
goto end;
|
||||
}
|
||||
pubkey = X509_get_pubkey(cert);
|
||||
if (!pubkey || pubkey->type != EVP_PKEY_RSA) {
|
||||
memcpy(CardCert->buf, p15cert->data, p15cert->data_len);
|
||||
|
||||
/* Parse user certificate just once into a x509 structure
|
||||
and not for each individual operation */
|
||||
if (!(CardCert->cert = certParseCertificate(CardCert->buf, CardCert->len))) {
|
||||
scam_print_msg(sctx, "certParseCertificate failed: invalid certificate.\n");
|
||||
goto end;
|
||||
}
|
||||
snprintf(data->scldap_entry, MAX_ENTRYLEN, "%s %s auth certificate", data->p15card->label, data->p15card->manufacturer_id);
|
||||
modify_base(sctx, data->scldap_entry, certGetSubject(CardCert->cert));
|
||||
r = scldap_search(data->lctx, data->scldap_entry, &lresult, 1, user);
|
||||
if ((r < 0) || (copy_result(lresult, &Cert->buf, &Cert->len) < 0)) {
|
||||
scam_print_msg(sctx, "Search failed: no auth certificate found.\n");
|
||||
goto end;
|
||||
}
|
||||
scldap_free_result(lresult);
|
||||
lresult = NULL;
|
||||
if (memcmp(CardCert->buf, Cert->buf, Cert->len) != 0) {
|
||||
scam_print_msg(sctx, "Certificate comparing failed.\n");
|
||||
goto end;
|
||||
}
|
||||
certFree(CardCert);
|
||||
CardCert = NULL;
|
||||
|
||||
/* Parse user certificate just once into a x509 structure
|
||||
and not for each individual operation */
|
||||
if (!(Cert->cert = certParseCertificate(Cert->buf, Cert->len))) {
|
||||
scam_print_msg(sctx, "certParseCertificate failed: invalid certificate.\n");
|
||||
goto end;
|
||||
}
|
||||
/* Do not accept self signed user certificates or certificates
|
||||
without issuer or subject fields */
|
||||
if (certIsSelfSigned(Cert->cert) != 0) {
|
||||
scam_print_msg(sctx, "certIsSelfSigned failed: certificate is not signed by a CA.\n");
|
||||
goto end;
|
||||
}
|
||||
/* We want an encipherment key */
|
||||
if ((r = certCheckKeyUsage(Cert->cert, DATA_ENCIPHERMENT)) < 1) {
|
||||
scam_print_msg(sctx, "certCheckKeyUsage failed: certificate cannot be used for encipherment.\n");
|
||||
if (r == -1) {
|
||||
scam_log_msg(sctx, "KeyUsage check failed (user %s).\n", user);
|
||||
} else {
|
||||
scam_log_msg(sctx, "Wrong certificate type (user %s).\n", user);
|
||||
}
|
||||
goto end;
|
||||
}
|
||||
if (!(Cert->pubkey = certParsePublicKey(Cert->cert))) {
|
||||
scam_print_msg(sctx, "certParsePublicKey failed: invalid public key in certificate.\n");
|
||||
scam_log_msg(sctx, "Invalid public key (user %s).\n", user);
|
||||
goto end;
|
||||
}
|
||||
if (Cert->pubkey->type != EVP_PKEY_RSA) {
|
||||
scam_log_msg(sctx, "Invalid public key. (user %s)\n", user);
|
||||
goto end;
|
||||
}
|
||||
chglen = RSA_size(pubkey->pkey.rsa);
|
||||
chglen = RSA_size(Cert->pubkey->pkey.rsa);
|
||||
if (chglen > sizeof(chg)) {
|
||||
scam_print_msg(sctx, "RSA key too big.\n");
|
||||
goto end;
|
||||
}
|
||||
/* Parse issuer from each one certificate
|
||||
in turn when you get them from the LDAP serer and stuff them
|
||||
all into the chain when we have other certificates than FINEID
|
||||
until issuer == subject. */
|
||||
do {
|
||||
char *distpoint = NULL;
|
||||
|
||||
/* Find empty slot */
|
||||
for (i = 0; i < MAX_PATHLEN; i++) {
|
||||
if (!(CACerts[i])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Path length exceeded */
|
||||
if (i == MAX_PATHLEN) {
|
||||
goto end;
|
||||
}
|
||||
CACerts[i] = certAlloc();
|
||||
if (!(CACerts[i])) {
|
||||
goto end;
|
||||
}
|
||||
snprintf(data->scldap_entry, MAX_ENTRYLEN, "%s %s ca certificate", data->p15card->label, data->p15card->manufacturer_id);
|
||||
modify_base(sctx, data->scldap_entry, certGetIssuer(Cert->cert));
|
||||
r = scldap_search(data->lctx, data->scldap_entry, &lresult, 1, NULL);
|
||||
if ((r < 0) || (copy_result(lresult, &CACerts[i]->buf, &CACerts[i]->len) < 0)) {
|
||||
scam_print_msg(sctx, "Search failed: no CA certificate.\n");
|
||||
goto end;
|
||||
}
|
||||
scldap_free_result(lresult);
|
||||
lresult = NULL;
|
||||
|
||||
/* Parse CA certificate into a x509 structure */
|
||||
if (!(CACerts[i]->cert = certParseCertificate(CACerts[i]->buf, CACerts[i]->len))) {
|
||||
scam_print_msg(sctx, "certParseCertificate failed: invalid CA certificate.\n");
|
||||
goto end;
|
||||
}
|
||||
distpoint = certGetCRLDistributionPoint(Cert->cert);
|
||||
snprintf(data->scldap_entry, MAX_ENTRYLEN, "%s %s crl", data->p15card->label, data->p15card->manufacturer_id);
|
||||
|
||||
if (scldap_is_valid_url(distpoint)) {
|
||||
if (scldap_url_to_entry(data->lctx, data->scldap_entry, distpoint) < 0) {
|
||||
scam_print_msg(sctx, "scldap_url_to_entry failed: invalid CRL.\n");
|
||||
free(distpoint);
|
||||
distpoint = NULL;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
free(distpoint);
|
||||
distpoint = NULL;
|
||||
r = scldap_search(data->lctx, data->scldap_entry, &lresult, 1, NULL);
|
||||
if ((r < 0) || (copy_result(lresult, &CACerts[i]->crlbuf, &CACerts[i]->crllen) < 0)) {
|
||||
scam_print_msg(sctx, "Search failed: CRL not found.\n");
|
||||
goto end;
|
||||
}
|
||||
scldap_free_result(lresult);
|
||||
lresult = NULL;
|
||||
|
||||
if (!(CACerts[i]->crl = certParseCRL(CACerts[i]->crlbuf, CACerts[i]->crllen))) {
|
||||
scam_print_msg(sctx, "certParseCRL failed: invalid CRL.\n");
|
||||
scam_log_msg(sctx, "Could not parse CA CRL.\n");
|
||||
goto end;
|
||||
}
|
||||
currcert = CACerts[i]->cert;
|
||||
} while (!certIsSelfSigned(currcert));
|
||||
|
||||
if ((r = certVerifyCAChain(CACerts, Cert->cert)) != 0) {
|
||||
scam_print_msg(sctx, "certVerifyCAChain failed: certificate has invalid information.\n");
|
||||
scam_log_msg(sctx, "certVerifyCAChain failed: %s.\n", certError((unsigned long) r));
|
||||
goto end;
|
||||
}
|
||||
certFreeAll(CACerts);
|
||||
|
||||
r = scrandom_get_data(random_data, sizeof(random_data));
|
||||
if (r < 0) {
|
||||
scam_log_msg(sctx, "scrandom_get_data failed.\n");
|
||||
|
@ -277,17 +459,18 @@ int p15_ldap_auth(scam_context * sctx, int argc, const char **argv,
|
|||
goto end;
|
||||
}
|
||||
|
||||
r = RSA_verify(NID_sha1, random_data, 20, chg, chglen, pubkey->pkey.rsa);
|
||||
r = RSA_verify(NID_sha1, random_data, 20, chg, chglen, Cert->pubkey->pkey.rsa);
|
||||
if (r != 1) {
|
||||
scam_print_msg(sctx, "Signature verification failed.\n");
|
||||
goto end;
|
||||
}
|
||||
err = SCAM_SUCCESS;
|
||||
end:
|
||||
if (pubkey)
|
||||
EVP_PKEY_free(pubkey);
|
||||
if (cert)
|
||||
X509_free(cert);
|
||||
if (CardCert)
|
||||
certFree(CardCert);
|
||||
if (Cert)
|
||||
certFree(Cert);
|
||||
certFreeAll(CACerts);
|
||||
if (p15cert)
|
||||
sc_pkcs15_free_certificate(p15cert);
|
||||
return err;
|
||||
|
@ -300,6 +483,10 @@ void p15_ldap_deinit(scam_context * sctx)
|
|||
if (!sctx->method_data) {
|
||||
return;
|
||||
}
|
||||
if (data->scldap_entry) {
|
||||
free(data->scldap_entry);
|
||||
}
|
||||
data->scldap_entry = NULL;
|
||||
if (data->lctx) {
|
||||
scldap_free_parameters(data->lctx);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue