summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Sommerseth <dazo@users.sourceforge.net>2008-12-20 02:26:33 +0100
committerDavid Sommerseth <dazo@users.sourceforge.net>2008-12-20 02:26:33 +0100
commit31148e445a38e6daac0331d3839037dc297cd359 (patch)
tree660abe72a7153f980145a64273e76f03bfdaaa78
parent726a4e4c303d1e553e342491e3700162d982fe10 (diff)
downloadeurephia-31148e445a38e6daac0331d3839037dc297cd359.tar.gz
eurephia-31148e445a38e6daac0331d3839037dc297cd359.tar.xz
eurephia-31148e445a38e6daac0331d3839037dc297cd359.zip
certs command: Can parse SSL certificate files when registering cert.
Instead of giving digest, common name, organisation and email address as parameters when registering a new certificate, the function can now take both PEM formated and PKCS12 formated certificate files and extract the needed information automatically. This functionality will only be enabled if OpenSSL v0.9.8 or newer is found.
-rw-r--r--CMakeLists.txt8
-rw-r--r--eurephiadm/commands/certificates.c215
2 files changed, 203 insertions, 20 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2e84238..0dcd875 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -28,7 +28,13 @@ IF(ADMIN_ENABLED)
pkg_search_module(LIBXML2 REQUIRED libxml-2.0 libxml2 libxml>=2.6)
INCLUDE_DIRECTORIES(BEFORE ${LIBXML2_INCLUDE_DIRS})
ADD_DEFINITIONS(-DHAVE_LIBXML2)
- SET(EXTRA_LIBS ${LIBXML2_LIBRARIES})
+ SET(EXTRA_LIBS ${EXTRA_LIBS} ${LIBXML2_LIBRARIES})
+
+ pkg_search_module(OPENSSL OPTIONAL openssl>=0.9.8)
+ IF(OPENSSL_FOUND)
+ ADD_DEFINITIONS(-DHAVE_OPENSSL)
+ SET(EXTRA_LIBS ${EXTRA_LIBS} ${OPENSSL_LIBRARIES})
+ ENDIF(OPENSSL_FOUND)
ENDIF(ADMIN_ENABLED)
IF(NOT DATABASE)
diff --git a/eurephiadm/commands/certificates.c b/eurephiadm/commands/certificates.c
index 3b87e28..a3ae77c 100644
--- a/eurephiadm/commands/certificates.c
+++ b/eurephiadm/commands/certificates.c
@@ -29,6 +29,13 @@
#include <errno.h>
#include <assert.h>
+#ifdef HAVE_OPENSSL
+#include <openssl/ssl.h>
+#include <openssl/pkcs12.h>
+#include <openssl/evp.h>
+#include <openssl/err.h>
+#endif
+
#ifdef HAVE_LIBXML2
#include <libxml/parser.h>
#include <libxml/tree.h>
@@ -47,6 +54,7 @@
#include <eurephiadb_driver.h>
#include "../argparser.h"
+#include "../get_console_input.h"
void display_certs_help(int page) {
printf("Help page not implemented yet\n");
@@ -73,12 +81,75 @@ int help_Certificates2(eurephiaCTX *ctx, eurephiaSESSION *sess, eurephiaVALUES *
return 0;
}
+#ifdef HAVE_OPENSSL
+char *ExtractCertInfo(X509 *cert, const char *fieldname) {
+ unsigned char *buf = (unsigned char *)1; // Needs to be 1 to avoid OpenSSL 0.9.6b bug
+ char resbuf[2048];
+ X509_NAME *name = NULL;
+ X509_NAME_ENTRY *namentry = NULL;
+ ASN1_STRING *asn1 = NULL;
+ int nid, tmp = -1, pos = -1;
+
+ //
+ // Extract subject information
+ //
+
+ memset(&resbuf, 0, 2048);
+
+ nid = OBJ_txt2nid(fieldname);
+ name = X509_get_subject_name(cert);
+
+ do {
+ pos = tmp;
+ tmp = X509_NAME_get_index_by_NID(name, nid, pos);
+ } while ( tmp > -1 );
+
+ if( pos == -1 ) {
+ fprintf(stderr, "%s: Field '%s' not found\n", MODULE, fieldname);
+ return NULL;
+ }
+
+ if( !(namentry = X509_NAME_get_entry(name, pos)) ) {
+ fprintf(stderr, "%s: Failed to extract name entry from field '%s'\n", MODULE, fieldname);
+ return NULL;
+ }
+
+ if( !(asn1 = X509_NAME_ENTRY_get_data(namentry)) ) {
+ fprintf(stderr, "%s: Failed to extract data from name entry field '%s'\n", MODULE, fieldname);
+ return NULL;
+ }
+
+ if( ASN1_STRING_to_UTF8(&buf, asn1) <= 0 ) {
+ fprintf(stderr, "%s: Failed to convert ASN1 string to UTF-8 for '%s'\n", MODULE, fieldname);
+ return NULL;
+ }
+
+ snprintf(resbuf, 2046, "%s", buf);
+ OPENSSL_free(buf);
+
+ return strdup_nullsafe(resbuf);
+}
+#endif
+
+void replace_char(char *str, char s, char r) {
+ if( str != NULL ) {
+ char *ptr = str;
+
+ while( *ptr != '\0' ) {
+ if( *ptr == s ) {
+ *ptr = r;
+ }
+ ptr++;
+ }
+ }
+}
+
int register_certificate(eurephiaCTX *ctx, int depth, const char *digest,
const char *cname, const char *org, const char *email)
{
xmlDoc *cert_xml = NULL;
xmlNode *cert_n = NULL;
- char tmp[66];
+ char tmp[66], *cname_cp = NULL, *org_cp = NULL;
int rc = 0;
assert( ctx != NULL );
@@ -92,26 +163,34 @@ int register_certificate(eurephiaCTX *ctx, int depth, const char *digest,
memset(&tmp, 0, 66);
snprintf(tmp, 64, "%i", depth);
+ cname_cp = strdup_nullsafe(cname);
+ replace_char(cname_cp, ' ', '_');
+ org_cp = strdup_nullsafe(org);
+ replace_char(org_cp, ' ', '_');
+
xmlNewChild(cert_n, NULL, (xmlChar *) "depth", (xmlChar *) tmp);
xmlNewChild(cert_n, NULL, (xmlChar *) "digest", (xmlChar *) digest);
- xmlNewChild(cert_n, NULL, (xmlChar *) "common_name", (xmlChar *) cname);
- xmlNewChild(cert_n, NULL, (xmlChar *) "organisation", (xmlChar *) org);
+ xmlNewChild(cert_n, NULL, (xmlChar *) "common_name", (xmlChar *) cname_cp);
+ xmlNewChild(cert_n, NULL, (xmlChar *) "organisation", (xmlChar *) org_cp);
xmlNewChild(cert_n, NULL, (xmlChar *) "email", (xmlChar *) email);
// Register the certificate
- xmlSaveFormatFileEnc("-", cert_xml, "UTF-8", 1);
rc = eDBadminAddCertificate(ctx, cert_xml);
fprintf(stdout, "%s: %s\n", MODULE,
(rc == 1 ? "Certificate registered successfully" : "Failed to register certificate"));
xmlFreeDoc(cert_xml);
+ free_nullsafe(cname_cp);
+ free_nullsafe(org_cp);
return (rc != 1);
}
int add_cert(eurephiaCTX *ctx, eurephiaSESSION *sess, eurephiaVALUES *cfg, int argc, char **argv) {
char *digest = NULL, *cname = NULL, *org = NULL, *email = NULL, *certfile = NULL;
- int depth = -1, i = 0, j = 0, chk = 0, certfile_format = 0;
+ int depth = -1, i = 0, j = 0, chk = 0, certfile_format = 0, rc = 0;
+#ifdef HAVE_OPENSSL
struct stat cert_stat;
+#endif
e_options addcertargs[] = {
{"--depth", "-d", 1},
@@ -119,12 +198,16 @@ int add_cert(eurephiaCTX *ctx, eurephiaSESSION *sess, eurephiaVALUES *cfg, int a
{"--common-name", "-C", 1},
{"--organisation", "-O", 1},
{"--email", "-E", 1},
+#ifdef HAVE_OPENSSL
{"--certfile", "-f", 1},
{"--pkcs12", "-p", 0},
+#endif
{"--help", "-h", 0},
{NULL, NULL, 0}
};
+ certfile = NULL;
+ certfile_format = 0;
for( i = 1; i < argc ; i++ ) {
switch( eurephia_getopt(&i, argc, argv, addcertargs) ) {
case 'd': // Certificate depth
@@ -150,18 +233,23 @@ int add_cert(eurephiaCTX *ctx, eurephiaSESSION *sess, eurephiaVALUES *cfg, int a
break;
case 'D': // Certificate digest
- digest = optargs[0];
+ if( strlen_nullsafe(optargs[0]) < 59 ) {
+ fprintf(stderr, "%s: Certificate digest is too short\n", MODULE);
+ return 1;
+ }
+ digest = strdup_nullsafe(optargs[0]);
break;
case 'C': // Certificate - common_name / CN
- cname = optargs[0];
+ cname = strdup_nullsafe(optargs[0]);
break;
case 'O': // Certificate - organisation / O
- org = optargs[0];
+ org = strdup_nullsafe(optargs[0]);
break;
case 'E': // Certificate - e-mail / emailAddr
- email = optargs[0];
+ email = strdup_nullsafe(optargs[0]);
break;
+#ifdef HAVE_OPENSSL
case 'p': // Certfile is in PKCS#12 format
certfile_format = 1;
break;
@@ -183,7 +271,8 @@ int add_cert(eurephiaCTX *ctx, eurephiaSESSION *sess, eurephiaVALUES *cfg, int a
fprintf(stderr, "%s: certfile '%s' is empty\n", MODULE, certfile);
return 1;
}
-
+ break;
+#endif
case 'h':
display_certs_help('A');
break;
@@ -193,39 +282,127 @@ int add_cert(eurephiaCTX *ctx, eurephiaSESSION *sess, eurephiaVALUES *cfg, int a
}
// Sanity check of input parameters
- if( (certfile != NULL) && ((depth>-1) || (digest!=NULL)|| (cname!=NULL) || (org!=NULL) || (email!=NULL)) ){
+ if( depth < 0 ) {
+ fprintf(stderr, "%s: You must set certificate depth (possibly, it needs to be 0)\n", MODULE);
+ return 1;
+ }
+
+ if( (certfile != NULL) && ((digest != NULL) || (cname != NULL) || (org != NULL) || (email != NULL)) ){
fprintf(stderr,
"%s: You cannot combine --certfile with --depth, --common-name,\n"
"--organisation or --email\n", MODULE);
return 1;
}
- if( (certfile == NULL) && ((depth<0)||(digest==NULL)||(cname==NULL)||(org==NULL)||(email==NULL)) ) {
+ if( (certfile == NULL) && ((digest == NULL) || (cname == NULL) || (org == NULL) || (email == NULL)) ) {
fprintf(stderr,
"%s: You must use either --certfile or --depth, --common-name,\n"
"--organisation and --email\n", MODULE);
return 1;
}
- if( strlen_nullsafe(digest) < 59 ) {
- fprintf(stderr, "%s: Certificate digest is too short\n", MODULE);
- return 1;
- }
-
+#ifdef HAVE_OPENSSL
// If we have a certfile - open it and fetch the info we want
if( certfile != NULL ) {
+ BIO *bio_err = NULL;
+ PKCS12 *p12 = NULL;
+ EVP_PKEY *pkey = NULL;
+ X509 *cert = NULL;
+ FILE *fp;
+
+ /* Needed to convert X509 digest into hex string */
+ unsigned char md_sha1[EVP_MAX_MD_SIZE];
+ unsigned int mdlen;
+
+ if( !bio_err ) {
+ SSL_library_init();
+ SSL_load_error_strings();
+ bio_err = BIO_new_fp(stderr, BIO_NOCLOSE);
+ }
+
+ // Open file - according to defined format
switch( certfile_format ) {
case 0: // PEM/DER format
+ fp = fopen(certfile, "r");
+ if( !(cert = PEM_read_X509(fp, NULL, NULL, NULL)) ) {
+ fprintf(stderr, "%s: Failed to open certificate file\n", MODULE);
+ return 3;
+ }
+ fclose(fp);
break;
+
case 1: // PKCS#12 format
+ fp = fopen(certfile, "r");
+ p12 = d2i_PKCS12_fp(fp, NULL);
+ fclose(fp);
+ if( p12 == NULL ) {
+ fprintf(stderr, "%s: Could not open PKCS#12 file\n", MODULE);
+ return 3;
+ }
+ OpenSSL_add_all_ciphers();
+
+ // First, try without password
+ if( !PKCS12_parse(p12, "", &pkey, &cert, NULL) ) {
+ char pwd[130];
+
+ // If empty password failed, get password and try again
+ memset(&pwd, 0, 130);
+ if( get_console_input(pwd, 128, "PKCS12 password:", 1) < 0 ) {
+ fprintf(stderr, "Could not retrieve password\n");
+ }
+ if( !PKCS12_parse(p12, pwd, &pkey, &cert, NULL) ) {
+ PKCS12_free(p12); p12 = NULL;
+ fprintf(stderr,
+ "%s: Could not open PKCS#12 file - wrong password\n", MODULE);
+ fprintf(stderr,
+ "%s: %s\n", MODULE, ERR_error_string(ERR_get_error(), NULL));
+ BIO_free(bio_err);
+ return 3;
+ }
+ }
+ EVP_PKEY_free(pkey); pkey = NULL;
+ PKCS12_free(p12); p12 = NULL;
break;
+
default: // Unknown
fprintf(stderr, "%s: Unknown certificate file format\n", MODULE);
return 1;
}
- }
- return register_certificate(ctx, depth, digest, cname, org, email);
+
+ // extract SHA1 digest from certificate
+ digest = (char *) malloc(66);
+ memset(digest, 0, 66);
+ if (X509_digest(cert, EVP_sha1(), md_sha1, &mdlen) && mdlen > 0) {
+ static const char hexcodes[] = "0123456789ABCDEF";
+ int j;
+
+ for (j = 0; j < (int) mdlen; j++) {
+ digest[j * 3] = hexcodes[(md_sha1[j] & 0xf0) >> 4U];
+ digest[(j * 3) + 1] = hexcodes[(md_sha1[j] & 0x0f)];
+ if (j + 1 != (int) mdlen) {
+ digest[(j * 3) + 2] = ':';
+ } else {
+ digest[(j * 3) + 2] = '\0';
+ }
+ }
+ }
+
+ // Extract the subject information we want
+ cname = ExtractCertInfo(cert, "CN");
+ org = ExtractCertInfo(cert, "O");
+ email = ExtractCertInfo(cert, "emailAddress");
+
+ X509_free(cert);
+ BIO_free(bio_err);
+ }
+#endif
+ rc = register_certificate(ctx, depth, digest, cname, org, email);
+ free_nullsafe(digest);
+ free_nullsafe(cname);
+ free_nullsafe(org);
+ free_nullsafe(email);
+ return rc;
}