summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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;
}