This repository has been archived on 2023-02-21. You can view files and clone it, but cannot push or open issues or pull requests.
idpc/src/auth.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];
}