Unixware doesn't have strings.h, so we need to declare strcasecmp()
[openssl.git] / apps / ca.c
index 481bfc77d758412c543b41ef79ef886d29ed31d1..db4652069697354dbb167dc9ffb192b7ba16850a 100644 (file)
--- a/apps/ca.c
+++ b/apps/ca.c
 #ifdef OPENSSL_SYS_WINDOWS
 #define strcasecmp _stricmp
 #else
-#include <strings.h>
+#  ifdef NO_STRINGS_H
+    int        strcasecmp();
+#  else
+#    include <strings.h>
+#  endif /* NO_STRINGS_H */
 #endif
 
 #ifndef W_OK
@@ -90,7 +94,7 @@
 #    else
 #      include <unixlib.h>
 #    endif
-#  else
+#  elif !defined(OPENSSL_SYS_VXWORKS)
 #    include <sys/file.h>
 #  endif
 #endif
 #define ENV_DEFAULT_CRL_DAYS   "default_crl_days"
 #define ENV_DEFAULT_CRL_HOURS  "default_crl_hours"
 #define ENV_DEFAULT_MD         "default_md"
+#define ENV_DEFAULT_EMAIL_DN   "email_in_dn"
 #define ENV_PRESERVE           "preserve"
 #define ENV_POLICY             "policy"
 #define ENV_EXTENSIONS         "x509_extensions"
@@ -182,6 +187,7 @@ static char *ca_usage[]={
 " -spkac file     - File contains DN and signed public key and challenge\n",
 " -ss_cert file   - File contains a self signed cert to sign\n",
 " -preserveDN     - Don't re-order the DN\n",
+" -noemailDN      - Don't add the EMAIL field into certificate' subject\n",
 " -batch          - Don't ask questions\n",
 " -msie_hack      - msie modifications to handle all those universal strings\n",
 " -revoke file    - Revoke a certificate (given in file)\n",
@@ -211,32 +217,31 @@ static BIGNUM *load_serial(char *serialfile);
 static int save_serial(char *serialfile, BIGNUM *serial);
 static int certify(X509 **xret, char *infile,EVP_PKEY *pkey,X509 *x509,
                   const EVP_MD *dgst,STACK_OF(CONF_VALUE) *policy,TXT_DB *db,
-                  BIGNUM *serial, char *subj, char *startdate,char *enddate,
-                  long days, int batch, char *ext_sect, CONF *conf,int verbose,
-                  unsigned long certopt, unsigned long nameopt, int default_op,
-                  int ext_copy);
+                  BIGNUM *serial, char *subj, int email_dn, char *startdate,
+                  char *enddate, long days, int batch, char *ext_sect, CONF *conf,
+                  int verbose, unsigned long certopt, unsigned long nameopt,
+                  int default_op, int ext_copy);
 static int certify_cert(X509 **xret, char *infile,EVP_PKEY *pkey,X509 *x509,
                        const EVP_MD *dgst,STACK_OF(CONF_VALUE) *policy,
-                       TXT_DB *db, BIGNUM *serial, char *subj, char *startdate,
-                       char *enddate, long days, int batch, char *ext_sect,
-                       CONF *conf,int verbose, unsigned long certopt,
+                       TXT_DB *db, BIGNUM *serial, char *subj, int email_dn,
+                       char *startdate, char *enddate, long days, int batch,
+                       char *ext_sect, CONF *conf,int verbose, unsigned long certopt,
                        unsigned long nameopt, int default_op, int ext_copy,
                        ENGINE *e);
 static int certify_spkac(X509 **xret, char *infile,EVP_PKEY *pkey,X509 *x509,
                         const EVP_MD *dgst,STACK_OF(CONF_VALUE) *policy,
-                        TXT_DB *db, BIGNUM *serial,char *subj, char *startdate,
-                        char *enddate, long days, char *ext_sect,CONF *conf,
-                        int verbose, unsigned long certopt, unsigned long nameopt,
-                        int default_op, int ext_copy);
+                        TXT_DB *db, BIGNUM *serial,char *subj, int email_dn,
+                        char *startdate, char *enddate, long days, char *ext_sect,
+                        CONF *conf, int verbose, unsigned long certopt, 
+                        unsigned long nameopt, int default_op, int ext_copy);
 static int fix_data(int nid, int *type);
 static void write_new_certificate(BIO *bp, X509 *x, int output_der, int notext);
 static int do_body(X509 **xret, EVP_PKEY *pkey, X509 *x509, const EVP_MD *dgst,
        STACK_OF(CONF_VALUE) *policy, TXT_DB *db, BIGNUM *serial,char *subj,
-       char *startdate, char *enddate, long days, int batch, int verbose,
-       X509_REQ *req, char *ext_sect, CONF *conf,
+       int email_dn, char *startdate, char *enddate, long days, int batch,
+               int verbose, X509_REQ *req, char *ext_sect, CONF *conf,
        unsigned long certopt, unsigned long nameopt, int default_op,
        int ext_copy);
-static X509_NAME *do_subject(char *subject);
 static int do_revoke(X509 *x509, TXT_DB *db, int ext, char *extval);
 static int get_certificate_status(const char *ser_status, TXT_DB *db);
 static int do_updatedb(TXT_DB *db);
@@ -268,6 +273,7 @@ int MAIN(int argc, char **argv)
        int total_done=0;
        int badops=0;
        int ret=1;
+       int email_dn=1;
        int req=0;
        int verbose=0;
        int gencrl=0;
@@ -294,6 +300,7 @@ int MAIN(int argc, char **argv)
        char *extensions=NULL;
        char *extfile=NULL;
        char *subj=NULL;
+       char *tmp_email_dn=NULL;
        char *crl_ext=NULL;
        int rev_type = REV_NONE;
        char *rev_arg = NULL;
@@ -439,6 +446,8 @@ EF_ALIGNMENT=0;
                        batch=1;
                else if (strcmp(*argv,"-preserveDN") == 0)
                        preserve=1;
+               else if (strcmp(*argv,"-noemailDN") == 0)
+                       email_dn=0;
                else if (strcmp(*argv,"-gencrl") == 0)
                        gencrl=1;
                else if (strcmp(*argv,"-msie_hack") == 0)
@@ -565,6 +574,7 @@ bad:
 #else
                strncpy(buf[0],X509_get_default_cert_area(),
                        sizeof(buf[0])-2-sizeof(CONFIG_FILE));
+               buf[0][sizeof(buf[0])-2-sizeof(CONFIG_FILE)]='\0';
                strcat(buf[0],"/");
 #endif
                strcat(buf[0],CONFIG_FILE);
@@ -584,6 +594,9 @@ bad:
                goto err;
                }
 
+       if (!load_config(bio_err, conf))
+               goto err;
+
        /* Lets get the config section we are using */
        if (section == NULL)
                {
@@ -1041,6 +1054,12 @@ bad:
                        lookup_fail(section,ENV_DEFAULT_MD);
                        goto err;
                        }
+               if ((email_dn == 1) && ((tmp_email_dn=NCONF_get_string(conf,
+                       section,ENV_DEFAULT_EMAIL_DN)) != NULL ))
+                       {
+                       if(strcmp(tmp_email_dn,"no") == 0)
+                               email_dn=0;
+                       }
                if ((dgst=EVP_get_digestbyname(md)) == NULL)
                        {
                        BIO_printf(bio_err,"%s is an unsupported message digest type\n",md);
@@ -1159,8 +1178,8 @@ bad:
                        {
                        total++;
                        j=certify_spkac(&x,spkac_file,pkey,x509,dgst,attribs,db,
-                               serial,subj,startdate,enddate, days,extensions,conf,
-                               verbose, certopt, nameopt, default_op, ext_copy);
+                               serial,subj,email_dn,startdate,enddate,days,extensions,
+                               conf,verbose,certopt,nameopt,default_op,ext_copy);
                        if (j < 0) goto err;
                        if (j > 0)
                                {
@@ -1183,7 +1202,7 @@ bad:
                        {
                        total++;
                        j=certify_cert(&x,ss_cert_file,pkey,x509,dgst,attribs,
-                               db,serial,subj,startdate,enddate,days,batch,
+                               db,serial,subj,email_dn,startdate,enddate,days,batch,
                                extensions,conf,verbose, certopt, nameopt,
                                default_op, ext_copy, e);
                        if (j < 0) goto err;
@@ -1203,7 +1222,7 @@ bad:
                        {
                        total++;
                        j=certify(&x,infile,pkey,x509,dgst,attribs,db,
-                               serial,subj,startdate,enddate,days,batch,
+                               serial,subj,email_dn,startdate,enddate,days,batch,
                                extensions,conf,verbose, certopt, nameopt,
                                default_op, ext_copy);
                        if (j < 0) goto err;
@@ -1223,7 +1242,7 @@ bad:
                        {
                        total++;
                        j=certify(&x,argv[i],pkey,x509,dgst,attribs,db,
-                               serial,subj,startdate,enddate,days,batch,
+                               serial,subj,email_dn,startdate,enddate,days,batch,
                                extensions,conf,verbose, certopt, nameopt,
                                default_op, ext_copy);
                        if (j < 0) goto err;
@@ -1262,6 +1281,7 @@ bad:
                        BIO_printf(bio_err,"Write out database with %d new entries\n",sk_X509_num(cert_sk));
 
                        strncpy(buf[0],serialfile,BSIZE-4);
+                       buf[0][BSIZE-4]='\0';
 
 #ifdef OPENSSL_SYS_VMS
                        strcat(buf[0],"-new");
@@ -1272,6 +1292,7 @@ bad:
                        if (!save_serial(buf[0],serial)) goto err;
 
                        strncpy(buf[1],dbfile,BSIZE-4);
+                       buf[1][BSIZE-4]='\0';
 
 #ifdef OPENSSL_SYS_VMS
                        strcat(buf[1],"-new");
@@ -1302,6 +1323,7 @@ bad:
                        p=(char *)x->cert_info->serialNumber->data;
                        
                        strncpy(buf[2],outdir,BSIZE-(j*2)-6);
+                       buf[2][BSIZE-(j*2)-6]='\0';
 
 #ifndef OPENSSL_SYS_VMS
                        strcat(buf[2],"/");
@@ -1339,6 +1361,7 @@ bad:
                        {
                        /* Rename the database and the serial file */
                        strncpy(buf[2],serialfile,BSIZE-4);
+                       buf[2][BSIZE-4]='\0';
 
 #ifdef OPENSSL_SYS_VMS
                        strcat(buf[2],"-old");
@@ -1367,6 +1390,7 @@ bad:
                                }
 
                        strncpy(buf[2],dbfile,BSIZE-4);
+                       buf[2][BSIZE-4]='\0';
 
 #ifdef OPENSSL_SYS_VMS
                        strcat(buf[2],"-old");
@@ -1489,6 +1513,11 @@ bad:
                        if (pkey->type == EVP_PKEY_DSA) 
                                dgst=EVP_dss1();
                        else
+#endif
+#ifndef OPENSSL_NO_ECDSA
+                       if (pkey->type == EVP_PKEY_ECDSA)
+                               dgst=EVP_ecdsa();
+                       else
 #endif
                                dgst=EVP_md5();
                        }
@@ -1534,6 +1563,7 @@ bad:
                        X509_free(revcert);
 
                        strncpy(buf[0],dbfile,BSIZE-4);
+                       buf[0][BSIZE-4]='\0';
 #ifndef OPENSSL_SYS_VMS
                        strcat(buf[0],".new");
 #else
@@ -1548,11 +1578,16 @@ bad:
                        j=TXT_DB_write(out,db);
                        if (j <= 0) goto err;
                        strncpy(buf[1],dbfile,BSIZE-4);
+                       buf[1][BSIZE-4]='\0';
 #ifndef OPENSSL_SYS_VMS
                        strcat(buf[1],".old");
 #else
                        strcat(buf[1],"-old");
 #endif
+                       BIO_free(in);
+                       in = NULL;
+                       BIO_free(out);
+                       out = NULL;
                        if (rename(dbfile,buf[1]) < 0)
                                {
                                BIO_printf(bio_err,"unable to rename %s to %s\n", dbfile, buf[1]);
@@ -1699,8 +1734,8 @@ err:
 
 static int certify(X509 **xret, char *infile, EVP_PKEY *pkey, X509 *x509,
             const EVP_MD *dgst, STACK_OF(CONF_VALUE) *policy, TXT_DB *db,
-            BIGNUM *serial, char *subj, char *startdate, char *enddate, long days,
-            int batch, char *ext_sect, CONF *lconf, int verbose,
+            BIGNUM *serial, char *subj, int email_dn, char *startdate, char *enddate,
+            long days, int batch, char *ext_sect, CONF *lconf, int verbose,
             unsigned long certopt, unsigned long nameopt, int default_op,
             int ext_copy)
        {
@@ -1749,8 +1784,8 @@ static int certify(X509 **xret, char *infile, EVP_PKEY *pkey, X509 *x509,
        else
                BIO_printf(bio_err,"Signature ok\n");
 
-       ok=do_body(xret,pkey,x509,dgst,policy,db,serial,subj,startdate, enddate,
-               days,batch,verbose,req,ext_sect,lconf,
+       ok=do_body(xret,pkey,x509,dgst,policy,db,serial,subj, email_dn,
+               startdate,enddate,days,batch,verbose,req,ext_sect,lconf,
                certopt, nameopt, default_op, ext_copy);
 
 err:
@@ -1761,8 +1796,8 @@ err:
 
 static int certify_cert(X509 **xret, char *infile, EVP_PKEY *pkey, X509 *x509,
             const EVP_MD *dgst, STACK_OF(CONF_VALUE) *policy, TXT_DB *db,
-            BIGNUM *serial, char *subj, char *startdate, char *enddate, long days,
-            int batch, char *ext_sect, CONF *lconf, int verbose,
+            BIGNUM *serial, char *subj, int email_dn, char *startdate, char *enddate,
+            long days, int batch, char *ext_sect, CONF *lconf, int verbose,
             unsigned long certopt, unsigned long nameopt, int default_op,
             int ext_copy, ENGINE *e)
        {
@@ -1803,9 +1838,9 @@ static int certify_cert(X509 **xret, char *infile, EVP_PKEY *pkey, X509 *x509,
        if ((rreq=X509_to_X509_REQ(req,NULL,EVP_md5())) == NULL)
                goto err;
 
-       ok=do_body(xret,pkey,x509,dgst,policy,db,serial,subj,startdate,enddate,days,
-               batch,verbose,rreq,ext_sect,lconf, certopt, nameopt, default_op,
-                       ext_copy);
+       ok=do_body(xret,pkey,x509,dgst,policy,db,serial,subj,email_dn,startdate,enddate,
+               days,batch,verbose,rreq,ext_sect,lconf, certopt, nameopt, default_op,
+               ext_copy);
 
 err:
        if (rreq != NULL) X509_REQ_free(rreq);
@@ -1815,12 +1850,12 @@ err:
 
 static int do_body(X509 **xret, EVP_PKEY *pkey, X509 *x509, const EVP_MD *dgst,
             STACK_OF(CONF_VALUE) *policy, TXT_DB *db, BIGNUM *serial, char *subj,
-            char *startdate, char *enddate, long days, int batch, int verbose,
-            X509_REQ *req, char *ext_sect, CONF *lconf,
+            int email_dn, char *startdate, char *enddate, long days, int batch,
+            int verbose, X509_REQ *req, char *ext_sect, CONF *lconf,
             unsigned long certopt, unsigned long nameopt, int default_op,
             int ext_copy)
        {
-       X509_NAME *name=NULL,*CAname=NULL,*subject=NULL;
+       X509_NAME *name=NULL,*CAname=NULL,*subject=NULL, *dn_subject=NULL;
        ASN1_UTCTIME *tm,*tmptm;
        ASN1_STRING *str,*str2;
        ASN1_OBJECT *obj;
@@ -1847,7 +1882,7 @@ static int do_body(X509 **xret, EVP_PKEY *pkey, X509 *x509, const EVP_MD *dgst,
 
        if (subj)
                {
-               X509_NAME *n = do_subject(subj);
+               X509_NAME *n = do_subject(subj, MBSTRING_ASC);
 
                if (!n)
                        {
@@ -1861,6 +1896,7 @@ static int do_body(X509 **xret, EVP_PKEY *pkey, X509 *x509, const EVP_MD *dgst,
 
        if (default_op)
                BIO_printf(bio_err,"The Subject's Distinguished Name is as follows\n");
+
        name=X509_REQ_get_subject_name(req);
        for (i=0; i<X509_NAME_entry_count(name); i++)
                {
@@ -1885,6 +1921,10 @@ static int do_body(X509 **xret, EVP_PKEY *pkey, X509 *x509, const EVP_MD *dgst,
                                str->type=V_ASN1_IA5STRING;
                        }
 
+               /* If no EMAIL is wanted in the subject */
+               if ((OBJ_obj2nid(obj) == NID_pkcs9_emailAddress) && (!email_dn))
+                       continue;
+
                /* check some things */
                if ((OBJ_obj2nid(obj) == NID_pkcs9_emailAddress) &&
                        (str->type != V_ASN1_IA5STRING))
@@ -1892,14 +1932,17 @@ static int do_body(X509 **xret, EVP_PKEY *pkey, X509 *x509, const EVP_MD *dgst,
                        BIO_printf(bio_err,"\nemailAddress type needs to be of type IA5STRING\n");
                        goto err;
                        }
-               j=ASN1_PRINTABLE_type(str->data,str->length);
-               if (    ((j == V_ASN1_T61STRING) &&
-                        (str->type != V_ASN1_T61STRING)) ||
-                       ((j == V_ASN1_IA5STRING) &&
-                        (str->type == V_ASN1_PRINTABLESTRING)))
+               if ((str->type != V_ASN1_BMPSTRING) && (str->type != V_ASN1_UTF8STRING))
                        {
-                       BIO_printf(bio_err,"\nThe string contains characters that are illegal for the ASN.1 type\n");
-                       goto err;
+                       j=ASN1_PRINTABLE_type(str->data,str->length);
+                       if (    ((j == V_ASN1_T61STRING) &&
+                                (str->type != V_ASN1_T61STRING)) ||
+                               ((j == V_ASN1_IA5STRING) &&
+                                (str->type == V_ASN1_PRINTABLESTRING)))
+                               {
+                               BIO_printf(bio_err,"\nThe string contains characters that are illegal for the ASN.1 type\n");
+                               goto err;
+                               }
                        }
 
                if (default_op)
@@ -2018,14 +2061,40 @@ again2:
        if (preserve)
                {
                X509_NAME_free(subject);
-               subject=X509_NAME_dup(X509_REQ_get_subject_name(req));
+               /* subject=X509_NAME_dup(X509_REQ_get_subject_name(req)); */
+               subject=X509_NAME_dup(name);
                if (subject == NULL) goto err;
                }
 
        if (verbose)
                BIO_printf(bio_err,"The subject name appears to be ok, checking data base for clashes\n");
 
-       row[DB_name]=X509_NAME_oneline(subject,NULL,0);
+       /* Build the correct Subject if no e-mail is wanted in the subject */
+       /* and add it later on because of the method extensions are added (altName) */
+        
+       if (email_dn)
+               dn_subject = subject;
+       else
+               {
+               X509_NAME_ENTRY *tmpne;
+               /* Its best to dup the subject DN and then delete any email
+                * addresses because this retains its structure.
+                */
+               if (!(dn_subject = X509_NAME_dup(subject)))
+                       {
+                       BIO_printf(bio_err,"Memory allocation failure\n");
+                       goto err;
+                       }
+               while((i = X509_NAME_get_index_by_NID(dn_subject,
+                                       NID_pkcs9_emailAddress, -1)) >= 0)
+                       {
+                       tmpne = X509_NAME_get_entry(dn_subject, i);
+                       X509_NAME_delete_entry(dn_subject, i);
+                       X509_NAME_ENTRY_free(tmpne);
+                       }
+               }
+
+       row[DB_name]=X509_NAME_oneline(dn_subject,NULL,0);
        row[DB_serial]=BN_bn2hex(serial);
        if ((row[DB_name] == NULL) || (row[DB_serial] == NULL))
                {
@@ -2181,6 +2250,11 @@ again2:
                goto err;
                }
 
+       /* Set the right value for the noemailDN option */
+       if( email_dn == 0 )
+               {
+               if (!X509_set_subject_name(ret,dn_subject)) goto err;
+               }
 
        if (!default_op)
                {
@@ -2219,6 +2293,16 @@ again2:
                EVP_PKEY_copy_parameters(pktmp,pkey);
        EVP_PKEY_free(pktmp);
 #endif
+#ifndef OPENSSL_NO_ECDSA
+       if (pkey->type == EVP_PKEY_ECDSA)
+               dgst = EVP_ecdsa();
+       pktmp = X509_get_pubkey(ret);
+       if (EVP_PKEY_missing_parameters(pktmp) &&
+               !EVP_PKEY_missing_parameters(pkey))
+               EVP_PKEY_copy_parameters(pktmp, pkey);
+       EVP_PKEY_free(pktmp);
+#endif
+
 
        if (!X509_sign(ret,pkey,dgst))
                goto err;
@@ -2275,6 +2359,8 @@ err:
                X509_NAME_free(CAname);
        if (subject != NULL)
                X509_NAME_free(subject);
+       if ((dn_subject != NULL) && !email_dn)
+               X509_NAME_free(dn_subject);
        if (tmptm != NULL)
                ASN1_UTCTIME_free(tmptm);
        if (ok <= 0)
@@ -2313,8 +2399,8 @@ static void write_new_certificate(BIO *bp, X509 *x, int output_der, int notext)
 
 static int certify_spkac(X509 **xret, char *infile, EVP_PKEY *pkey, X509 *x509,
             const EVP_MD *dgst, STACK_OF(CONF_VALUE) *policy, TXT_DB *db,
-            BIGNUM *serial, char *subj, char *startdate, char *enddate, long days,
-            char *ext_sect, CONF *lconf, int verbose, unsigned long certopt,
+            BIGNUM *serial, char *subj, int email_dn, char *startdate, char *enddate,
+            long days, char *ext_sect, CONF *lconf, int verbose, unsigned long certopt,
             unsigned long nameopt, int default_op, int ext_copy)
        {
        STACK_OF(CONF_VALUE) *sk=NULL;
@@ -2405,6 +2491,11 @@ static int certify_spkac(X509 **xret, char *infile, EVP_PKEY *pkey, X509 *x509,
                        continue;
                        }
 
+               /*
+               if ((nid == NID_pkcs9_emailAddress) && (email_dn == 0))
+                       continue;
+               */
+               
                j=ASN1_PRINTABLE_type((unsigned char *)buf,-1);
                if (fix_data(nid, &j) == 0)
                        {
@@ -2449,7 +2540,7 @@ static int certify_spkac(X509 **xret, char *infile, EVP_PKEY *pkey, X509 *x509,
 
        X509_REQ_set_pubkey(req,pktmp);
        EVP_PKEY_free(pktmp);
-       ok=do_body(xret,pkey,x509,dgst,policy,db,serial,subj,startdate,enddate,
+       ok=do_body(xret,pkey,x509,dgst,policy,db,serial,subj,email_dn,startdate,enddate,
                   days,1,verbose,req,ext_sect,lconf, certopt, nameopt, default_op,
                        ext_copy);
 err:
@@ -2935,65 +3026,124 @@ int make_revoked(X509_REVOKED *rev, char *str)
        return ret;
        }
 
-static X509_NAME *do_subject(char *subject)
+/*
+ * subject is expected to be in the format /type0=value0/type1=value1/type2=...
+ * where characters may be escaped by \
+ */
+X509_NAME *do_subject(char *subject, long chtype)
        {
-       X509_NAME *n = NULL;
-
-       int i, nid, ne_num=0;
+       size_t buflen = strlen(subject)+1; /* to copy the types and values into. due to escaping, the copy can only become shorter */
+       char *buf = OPENSSL_malloc(buflen);
+       size_t max_ne = buflen / 2 + 1; /* maximum number of name elements */
+       char **ne_types = OPENSSL_malloc(max_ne * sizeof (char *));
+       char **ne_values = OPENSSL_malloc(max_ne * sizeof (char *));
 
-       char *ne_name = NULL;
-       char *ne_value = NULL;
+       char *sp = subject, *bp = buf;
+       int i, ne_num = 0;
 
-       char *tmp = NULL;
-       char *p[2];
+       X509_NAME *n = NULL;
+       int nid;
 
-       char *str_list[256];
-       
-       p[0] = ",/";
-       p[1] = "=";
+       if (!buf || !ne_types || !ne_values)
+       {
+               BIO_printf(bio_err, "malloc error\n");
+               goto error;
+       }
 
-       n = X509_NAME_new();
+       if (*subject != '/')
+       {
+               BIO_printf(bio_err, "Subject does not start with '/'.\n");
+               goto error;
+       }
+       sp++; /* skip leading / */
 
-       tmp = strtok(subject, p[0]);
-       while((tmp != NULL) && (ne_num < (sizeof str_list/sizeof *str_list)))
+       while (*sp)
+       {
+               /* collect type */
+               ne_types[ne_num] = bp;
+               while (*sp)
                {
-               char *token = tmp;
-
-               while (token[0] == ' ')
-                       token++;
-               str_list[ne_num] = token;
-
-               tmp = strtok(NULL, p[0]);
-               ne_num++;
+                       if (*sp == '\\') /* is there anything to escape in the type...? */
+                               if (*++sp)
+                                       *bp++ = *sp++;
+                               else
+                               {
+                                       BIO_printf(bio_err, "escape character at end of string\n");
+                                       goto error;
+                               }
+                       else if (*sp == '=')
+                       {
+                               sp++;
+                               *bp++ = '\0';
+                               break;
+                       }
+                       else
+                               *bp++ = *sp++;
                }
+               if (!*sp)
+               {
+                       BIO_printf(bio_err, "end of string encountered while processing type of subject name element #%d\n", ne_num);
+                       goto error;
+               }
+               ne_values[ne_num] = bp;
+               while (*sp)
+               {
+                       if (*sp == '\\')
+                               if (*++sp)
+                                       *bp++ = *sp++;
+                               else
+                               {
+                                       BIO_printf(bio_err, "escape character at end of string\n");
+                                       goto error;
+                               }
+                       else if (*sp == '/')
+                       {
+                               sp++;
+                               break;
+                       }
+                       else
+                               *bp++ = *sp++;
+               }
+               *bp++ = '\0';
+               ne_num++;
+       }
+
+       if (!(n = X509_NAME_new()))
+               goto error;
 
        for (i = 0; i < ne_num; i++)
                {
-               ne_name  = strtok(str_list[i], p[1]);
-               ne_value = strtok(NULL, p[1]);
-
-               if ((nid=OBJ_txt2nid(ne_name)) == NID_undef)
+               if ((nid=OBJ_txt2nid(ne_types[i])) == NID_undef)
                        {
-                       BIO_printf(bio_err, "Subject Attribute %s has no known NID, skipped\n", ne_name);
+                       BIO_printf(bio_err, "Subject Attribute %s has no known NID, skipped\n", ne_types[i]);
                        continue;
                        }
 
-               if (ne_value == NULL)
+               if (!*ne_values[i])
                        {
-                       BIO_printf(bio_err, "No value provided for Subject Attribute %s, skipped\n", ne_name);
+                       BIO_printf(bio_err, "No value provided for Subject Attribute %s, skipped\n", ne_types[i]);
                        continue;
                        }
 
-               if (!X509_NAME_add_entry_by_NID(n, nid, MBSTRING_ASC, (unsigned char*)ne_value, -1,-1,0))
-                       {
-                       X509_NAME_free(n);
-                       return NULL;
-                       }
+               if (!X509_NAME_add_entry_by_NID(n, nid, chtype, (unsigned char*)ne_values[i], -1,-1,0))
+                       goto error;
                }
 
+       OPENSSL_free(ne_values);
+       OPENSSL_free(ne_types);
+       OPENSSL_free(buf);
        return n;
-       }
 
+error:
+       X509_NAME_free(n);
+       if (ne_values)
+               OPENSSL_free(ne_values);
+       if (ne_types)
+               OPENSSL_free(ne_types);
+       if (buf)
+               OPENSSL_free(buf);
+       return NULL;
+}
 
 int old_entry_print(BIO *bp, ASN1_OBJECT *obj, ASN1_STRING *str)
        {