diff options
Diffstat (limited to 'eurephiadm/commands/certificates.c')
-rw-r--r-- | eurephiadm/commands/certificates.c | 215 |
1 files changed, 196 insertions, 19 deletions
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; } |