283 lines
6.6 KiB
C
283 lines
6.6 KiB
C
/*
|
|
* idpc - IDP as a C CGI program
|
|
* Copyright (C) 2004 Entr'ouvert
|
|
*
|
|
* Author: Frederic Peters <fpeters@entrouvert.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#include "idpc.h"
|
|
|
|
/**
|
|
* get_ocsp_url
|
|
* @cert
|
|
*
|
|
* Description:
|
|
* Extract the OCSP URI of a certificate
|
|
* http://www.ietf.org/rfc/rfc2459.txt
|
|
* http://www.ietf.org/rfc/rfc2560.txt
|
|
**/
|
|
static char* get_ocsp_url(X509 *cert)
|
|
{
|
|
int i, j;
|
|
const char *sn;
|
|
char *result = NULL;
|
|
|
|
for (i=0; i < X509_get_ext_count(cert); i++) {
|
|
STACK_OF(CONF_VALUE) *values = NULL;
|
|
X509V3_EXT_METHOD *meth = NULL;
|
|
X509_EXTENSION *extension = NULL;
|
|
ASN1_OBJECT *obj;
|
|
int nid;
|
|
|
|
extension = X509_get_ext(cert, i);
|
|
obj = X509_EXTENSION_get_object(extension);
|
|
if (obj == NULL) {
|
|
continue;
|
|
}
|
|
|
|
nid = OBJ_obj2nid(obj);
|
|
if (nid == NID_undef) {
|
|
continue;
|
|
}
|
|
|
|
sn = OBJ_nid2sn(nid);
|
|
if ( sn == NULL || strcmp(sn, "authorityInfoAccess") != 0 ) {
|
|
continue;
|
|
}
|
|
|
|
meth = X509V3_EXT_get(extension);
|
|
if (meth == NULL) {
|
|
continue;
|
|
}
|
|
|
|
values = meth->i2v(meth, X509V3_EXT_d2i(extension), 0);
|
|
if (values == NULL) {
|
|
continue;
|
|
}
|
|
|
|
for (j=0; j < sk_CONF_VALUE_num(values); j++) {
|
|
CONF_VALUE *value = sk_CONF_VALUE_value(values, j);
|
|
if (strcmp(value->name, "OCSP - URI") == 0) {
|
|
result = strdup(value->value);
|
|
break;
|
|
}
|
|
}
|
|
sk_CONF_VALUE_free(values);
|
|
|
|
/* there is only one authorityInfoAccess and we got it */
|
|
break;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
static int ocsp_check(char *client_cert)
|
|
{
|
|
/* verify certificate through OCSP */
|
|
/* "logic" taken from openssl apps/ocsp.c */
|
|
X509 *issuer = NULL, *cert = NULL;
|
|
FILE *fcert;
|
|
OCSP_RESPONSE *resp = NULL;
|
|
OCSP_BASICRESP *bs = NULL;
|
|
OCSP_REQUEST *req = NULL;
|
|
X509_STORE *store;
|
|
X509_STORE_CTX store_ctx;
|
|
char *host = NULL, *port = NULL, *path = "/";
|
|
int use_ssl = -1;
|
|
BIO *cbio = NULL, *sbio = NULL;
|
|
SSL_CTX *ctx = NULL;
|
|
OCSP_CERTID *id;
|
|
int status, reason;
|
|
ASN1_GENERALIZEDTIME *rev, *thisupd, *nextupd;
|
|
char *ocsp_url;
|
|
|
|
/* OpenSSL read a certificate from a file; we create a temporary file
|
|
* and write down the client certificate */
|
|
fcert = tmpfile();
|
|
fputs(client_cert, fcert);
|
|
rewind(fcert);
|
|
if (!PEM_read_X509(fcert, &cert, NULL, NULL)) {
|
|
fprintf(stderr, "reading user cert failed\n");
|
|
return -1;
|
|
}
|
|
fclose(fcert);
|
|
|
|
/* get issuer */
|
|
store = X509_STORE_new();
|
|
X509_STORE_CTX_init(&store_ctx, store, NULL, NULL);
|
|
if (X509_STORE_CTX_get1_issuer(&issuer, &store_ctx, cert) != 1) {
|
|
fprintf(stderr,
|
|
"get1_issuer from cert failed; using config file\n");
|
|
if (get_config_string("//idpc:ocspIssuer") == NULL) {
|
|
fprintf(stderr, "no ocspIssuer set\n");
|
|
return -1;
|
|
}
|
|
fcert = fopen(get_config_string("//idpc:ocspIssuer"), "r");
|
|
if (!PEM_read_X509(fcert, &issuer, NULL, NULL)) {
|
|
fprintf(stderr, "reading issuer cert failed\n");
|
|
return -1;
|
|
}
|
|
fclose(fcert);
|
|
}
|
|
|
|
/* OCSP server URL */
|
|
ocsp_url = get_ocsp_url(cert);
|
|
if (ocsp_url == NULL) {
|
|
/* no OCSP url in certificate; maybe in config file ? */
|
|
ocsp_url = get_config_string("//idpc:ocspUrl");
|
|
if (ocsp_url == NULL) {
|
|
fprintf(stderr, "failed to get ocsp url");
|
|
return -1;
|
|
}
|
|
ocsp_url = strdup(ocsp_url);
|
|
}
|
|
|
|
if (OCSP_parse_url(ocsp_url, &host, &port, &path, &use_ssl) != 0) {
|
|
fprintf(stderr, "failed to parse ocsp url\n");
|
|
return -1;
|
|
}
|
|
|
|
|
|
cbio = BIO_new_connect(host);
|
|
if (!cbio) {
|
|
fprintf(stderr, "BIO_new_connect failed\n");
|
|
return -1;
|
|
}
|
|
|
|
if (port) {
|
|
BIO_set_conn_port(cbio, port);
|
|
}
|
|
if (use_ssl == 1) {
|
|
ctx = SSL_CTX_new(SSLv23_client_method());
|
|
SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY);
|
|
sbio = BIO_new_ssl(ctx, 1);
|
|
cbio = BIO_push(sbio, cbio);
|
|
}
|
|
if (BIO_do_connect(cbio) <= 0) {
|
|
BIO_free_all(cbio);
|
|
fprintf(stderr, "BIO_do_connect failed\n");
|
|
return -1;
|
|
}
|
|
|
|
id = OCSP_cert_to_id(NULL, cert, issuer);
|
|
if (id == NULL) {
|
|
BIO_free_all(cbio);
|
|
fprintf(stderr, "OCSP_cert_to_id failed\n");
|
|
return -1;
|
|
}
|
|
|
|
req = OCSP_REQUEST_new();
|
|
if (OCSP_request_add0_id(req, id)) {
|
|
BIO_free_all(cbio);
|
|
fprintf(stderr, "OCSP_request_add0_id failed\n");
|
|
return -1;
|
|
}
|
|
|
|
if (OCSP_request_add1_nonce(req, NULL, -1)) {
|
|
BIO_free_all(cbio);
|
|
fprintf(stderr, "OCSP_request_add1_nonce failed\n");
|
|
}
|
|
|
|
resp = OCSP_sendreq_bio(cbio, path, req);
|
|
BIO_free_all(cbio);
|
|
|
|
bs = OCSP_response_get1_basic(resp);
|
|
if (!bs) {
|
|
fprintf(stderr, "OCSP_response_get1_basic failed\n");
|
|
return -1;
|
|
}
|
|
|
|
status = OCSP_response_status(resp);
|
|
if (!OCSP_resp_find_status(bs, id, &status, &reason,
|
|
&rev, &thisupd, &nextupd)) {
|
|
fprintf(stderr, "OCSP_resp_find_status failed\n");
|
|
return -1;
|
|
}
|
|
fprintf(stderr, "OCSP status: %s\n", OCSP_cert_status_str(status));
|
|
|
|
if (OCSP_check_nonce(req, bs) <= 0) {
|
|
fprintf(stderr, "OCSP_check_nonce failed\n");
|
|
return -1;
|
|
}
|
|
|
|
X509_free(issuer);
|
|
X509_free(cert);
|
|
|
|
return status; /* refer to ocsp.h for possible values */
|
|
}
|
|
|
|
|
|
char* certificate_auth()
|
|
{
|
|
char *client_verify;
|
|
|
|
client_verify = getenv("SSL_CLIENT_VERIFY");
|
|
if (client_verify == NULL) {
|
|
fprintf(stderr, "no SSL_CLIENT_VERIFY variable\n");
|
|
return NULL;
|
|
}
|
|
|
|
if (strcmp(client_verify, "SUCCESS") != 0) {
|
|
fprintf(stderr, "Client certificate failed to verify (%s)\n",
|
|
client_verify);
|
|
return NULL;
|
|
}
|
|
|
|
if (strcmp(get_config_string("//idpc:ocspCheck"), "true") == 0 &&
|
|
ocsp_check(getenv("SSL_CLIENT_CERT")) != 0) {
|
|
fprintf(stderr, "ocsp check failed\n");
|
|
return NULL;
|
|
}
|
|
|
|
return strdup(getenv("SSL_CLIENT_M_SERIAL"));
|
|
}
|
|
|
|
char* http_auth()
|
|
{
|
|
char *str;
|
|
str = getenv("REMOTE_USER");
|
|
if (str) {
|
|
return strdup(str);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
struct authentication authentications[] = {
|
|
{"certificate", certificate_auth,
|
|
lassoSamlAuthenticationMethodSoftwarePki},
|
|
{"http", http_auth, lassoSamlAuthenticationMethodPassword},
|
|
{ NULL, NULL, NULL}
|
|
};
|
|
|
|
struct authentication* get_authentication(char *auth_method)
|
|
{
|
|
int i;
|
|
|
|
for (i=0; authentications[i].name && \
|
|
strcmp(authentications[i].name, auth_method) != 0; i++) ;
|
|
|
|
if (authentications[i].name == NULL ) {
|
|
fprintf(stderr, "no auth by that name\n");
|
|
return NULL;
|
|
}
|
|
|
|
return &authentications[i];
|
|
}
|
|
|