Initial automation changes to 'req' and X509_ATTRIBUTE functions.
diff --git a/CHANGES b/CHANGES
index 52e0ffb..7853e6c 100644
--- a/CHANGES
+++ b/CHANGES
@@ -4,6 +4,31 @@
 
  Changes between 0.9.4 and 0.9.5  [xx XXX 1999]
 
+  *) Initial changes to the 'req' utility to allow request generation
+     automation. This will allow an application to just generate a template
+     file containing all the field values and have req construct the
+     request.
+
+     Initial support for X509_ATTRIBUTE handling. Stacks of these are
+     used all over the place including certificate requests and PKCS#7
+     structures. They are currently handled manually where necessary with
+     some primitive wrappers for PKCS#7. The new functions behave in a
+     manner analagous to the X509 extension functions: they allow
+     attributes to be looked up by NID and added.
+
+     Later something similar to the X509V3 code would be desirable to
+     automatically handle the encoding, decoding and printing of the
+     more complex types. The string types like challengePassword can
+     be handled by the string table fuctions.
+
+     Also modified the multi byte string table handling. Now there is
+     a 'global mask' which masks out certain types. The table itself
+     can use the flag STABLE_NO_MASK to ignore the mask setting: this
+     is useful when for example there is only one permissible type
+     (as in countryName) and using the mask might result in no valid
+     types at all.
+     [Steve Henson]
+
   *) Clean up 'Finished' handling, and add functions SSL_get_finished and
      SSL_get_peer_finished to allow applications to obtain the latest
      Finished messages sent to the peer or expected from the peer,
diff --git a/apps/openssl.cnf b/apps/openssl.cnf
index 13aeb9b..dbe8cbe 100644
--- a/apps/openssl.cnf
+++ b/apps/openssl.cnf
@@ -95,16 +95,15 @@
 # input_password = secret
 # output_password = secret
 
-# This sets the permitted types in a DirectoryString. There are several
-# options. 
+# This sets a mask for permitted string types. There are several options. 
 # default: PrintableString, T61String, BMPString.
 # pkix	 : PrintableString, BMPString.
 # utf8only: only UTF8Strings.
-# nobmp : PrintableString, T61String (no BMPStrings).
+# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings).
 # MASK:XXXX a literal mask value.
 # WARNING: current versions of Netscape crash on BMPStrings or UTF8Strings
 # so use this option with caution!
-dirstring_type = nobmp
+string_mask = nombstr
 
 # req_extensions = v3_req # The extensions to add to a certificate request
 
diff --git a/apps/req.c b/apps/req.c
index 5c14c71..76a4514 100644
--- a/apps/req.c
+++ b/apps/req.c
@@ -78,11 +78,12 @@
 
 #define BITS		"default_bits"
 #define KEYFILE		"default_keyfile"
+#define PROMPT		"prompt"
 #define DISTINGUISHED_NAME	"distinguished_name"
 #define ATTRIBUTES	"attributes"
 #define V3_EXTENSIONS	"x509_extensions"
 #define REQ_EXTENSIONS	"req_extensions"
-#define DIRSTRING_TYPE	"dirstring_type"
+#define STRING_MASK	"string_mask"
 
 #define DEFAULT_KEY_LENGTH	512
 #define MIN_KEY_LENGTH		384
@@ -109,6 +110,11 @@
  */
 
 static int make_REQ(X509_REQ *req,EVP_PKEY *pkey,int attribs);
+static int prompt_info(X509_REQ *req,
+		STACK_OF(CONF_VALUE) *dn_sk, char *dn_sect,
+		STACK_OF(CONF_VALUE) *attr_sk, char *attr_sect, int attribs);
+static int auto_info(X509_REQ *req, STACK_OF(CONF_VALUE) *sk,
+				STACK_OF(CONF_VALUE) *attr, int attribs);
 static int add_attribute_object(STACK_OF(X509_ATTRIBUTE) *n, char *text,
 				char *def, char *value, int nid, int min,
 				int max);
@@ -491,10 +497,10 @@
 	if(!passout)
 		passout = CONF_get_string(req_conf, SECTION, "output_password");
 
-	p = CONF_get_string(req_conf, SECTION, DIRSTRING_TYPE);
+	p = CONF_get_string(req_conf, SECTION, STRING_MASK);
 
 	if(p && !ASN1_STRING_set_default_mask_asc(p)) {
-		BIO_printf(bio_err, "Invalid DiretoryString setting %s", p);
+		BIO_printf(bio_err, "Invalid global string mask setting %s", p);
 		goto end;
 	}
 
@@ -892,46 +898,47 @@
 static int make_REQ(X509_REQ *req, EVP_PKEY *pkey, int attribs)
 	{
 	int ret=0,i;
-	char *p,*q;
-	X509_REQ_INFO *ri;
-	char buf[100];
-	int nid,min,max;
-	char *type,*def,*tmp,*value,*tmp_attr;
-	STACK_OF(CONF_VALUE) *sk, *attr=NULL;
-	CONF_VALUE *v;
-	
-	tmp=CONF_get_string(req_conf,SECTION,DISTINGUISHED_NAME);
-	if (tmp == NULL)
+	char no_prompt = 0;
+	STACK_OF(CONF_VALUE) *dn_sk, *attr_sk = NULL;
+	char *tmp, *dn_sect,*attr_sect;
+
+	tmp=CONF_get_string(req_conf,SECTION,PROMPT);
+	if((tmp != NULL) && !strcmp(tmp, "no")) no_prompt = 1;
+
+	dn_sect=CONF_get_string(req_conf,SECTION,DISTINGUISHED_NAME);
+	if (dn_sect == NULL)
 		{
 		BIO_printf(bio_err,"unable to find '%s' in config\n",
 			DISTINGUISHED_NAME);
 		goto err;
 		}
-	sk=CONF_get_section(req_conf,tmp);
-	if (sk == NULL)
+	dn_sk=CONF_get_section(req_conf,dn_sect);
+	if (dn_sk == NULL)
 		{
-		BIO_printf(bio_err,"unable to get '%s' section\n",tmp);
+		BIO_printf(bio_err,"unable to get '%s' section\n",dn_sect);
 		goto err;
 		}
 
-	tmp_attr=CONF_get_string(req_conf,SECTION,ATTRIBUTES);
-	if (tmp_attr == NULL)
-		attr=NULL;
+	attr_sect=CONF_get_string(req_conf,SECTION,ATTRIBUTES);
+	if (attr_sect == NULL)
+		attr_sk=NULL;
 	else
 		{
-		attr=CONF_get_section(req_conf,tmp_attr);
-		if (attr == NULL)
+		attr_sk=CONF_get_section(req_conf,attr_sect);
+		if (attr_sk == NULL)
 			{
-			BIO_printf(bio_err,"unable to get '%s' section\n",tmp_attr);
+			BIO_printf(bio_err,"unable to get '%s' section\n",attr_sect);
 			goto err;
 			}
 		}
 
-	ri=req->req_info;
-
 	/* setup version number */
-	if (!ASN1_INTEGER_set(ri->version,0L)) goto err; /* version 1 */
+	if (!X509_REQ_set_version(req,0L)) goto err; /* version 1 */
 
+	if(no_prompt) i = auto_info(req, dn_sk, attr_sk, attribs);
+	else i = prompt_info(req, dn_sk, dn_sect, attr_sk, attr_sect, attribs);
+	if(!i) goto err;
+#if 0
 	BIO_printf(bio_err,"You are about to be asked to enter information that will be incorporated\n");
 	BIO_printf(bio_err,"into your certificate request.\n");
 	BIO_printf(bio_err,"What you are about to enter is what is called a Distinguished Name or a DN.\n");
@@ -1039,7 +1046,7 @@
 		BIO_printf(bio_err,"No template, please set one up.\n");
 		goto err;
 		}
-
+#endif
 	X509_REQ_set_pubkey(req,pkey);
 
 	ret=1;
@@ -1047,6 +1054,220 @@
 	return(ret);
 	}
 
+
+static int prompt_info(X509_REQ *req,
+		STACK_OF(CONF_VALUE) *dn_sk, char *dn_sect,
+		STACK_OF(CONF_VALUE) *attr_sk, char *attr_sect, int attribs)
+	{
+	int i;
+	char *p,*q;
+	char buf[100];
+	int nid,min,max;
+	char *type,*def,*value;
+	CONF_VALUE *v;
+	X509_NAME *subj;
+	subj = X509_REQ_get_subject_name(req);
+	BIO_printf(bio_err,"You are about to be asked to enter information that will be incorporated\n");
+	BIO_printf(bio_err,"into your certificate request.\n");
+	BIO_printf(bio_err,"What you are about to enter is what is called a Distinguished Name or a DN.\n");
+	BIO_printf(bio_err,"There are quite a few fields but you can leave some blank\n");
+	BIO_printf(bio_err,"For some fields there will be a default value,\n");
+	BIO_printf(bio_err,"If you enter '.', the field will be left blank.\n");
+	BIO_printf(bio_err,"-----\n");
+
+
+	if (sk_CONF_VALUE_num(dn_sk))
+		{
+		i= -1;
+start:		for (;;)
+			{
+			i++;
+			if (sk_CONF_VALUE_num(dn_sk) <= i) break;
+
+			v=sk_CONF_VALUE_value(dn_sk,i);
+			p=q=NULL;
+			type=v->name;
+			if(!check_end(type,"_min") || !check_end(type,"_max") ||
+				!check_end(type,"_default") ||
+					 !check_end(type,"_value")) continue;
+			/* Skip past any leading X. X: X, etc to allow for
+			 * multiple instances 
+			 */
+			for(p = v->name; *p ; p++) 
+				if ((*p == ':') || (*p == ',') ||
+							 (*p == '.')) {
+					p++;
+					if(*p) type = p;
+					break;
+				}
+			/* If OBJ not recognised ignore it */
+			if ((nid=OBJ_txt2nid(type)) == NID_undef) goto start;
+			sprintf(buf,"%s_default",v->name);
+			if ((def=CONF_get_string(req_conf,dn_sect,buf)) == NULL)
+				def="";
+				
+			sprintf(buf,"%s_value",v->name);
+			if ((value=CONF_get_string(req_conf,dn_sect,buf)) == NULL)
+				value=NULL;
+
+			sprintf(buf,"%s_min",v->name);
+			min=(int)CONF_get_number(req_conf,dn_sect,buf);
+
+			sprintf(buf,"%s_max",v->name);
+			max=(int)CONF_get_number(req_conf,dn_sect,buf);
+
+			if (!add_DN_object(subj,v->value,def,value,nid,
+				min,max))
+				return 0;
+			}
+		if (X509_NAME_entry_count(subj) == 0)
+			{
+			BIO_printf(bio_err,"error, no objects specified in config file\n");
+			return 0;
+			}
+
+		if (attribs)
+			{
+			if ((attr_sk != NULL) && (sk_CONF_VALUE_num(attr_sk) > 0))
+				{
+				BIO_printf(bio_err,"\nPlease enter the following 'extra' attributes\n");
+				BIO_printf(bio_err,"to be sent with your certificate request\n");
+				}
+
+			i= -1;
+start2:			for (;;)
+				{
+				i++;
+				if ((attr_sk == NULL) ||
+					    (sk_CONF_VALUE_num(attr_sk) <= i))
+					break;
+
+				v=sk_CONF_VALUE_value(attr_sk,i);
+				type=v->name;
+				if ((nid=OBJ_txt2nid(type)) == NID_undef)
+					goto start2;
+
+				sprintf(buf,"%s_default",type);
+				if ((def=CONF_get_string(req_conf,attr_sect,buf))
+					== NULL)
+					def="";
+				
+				sprintf(buf,"%s_value",type);
+				if ((value=CONF_get_string(req_conf,attr_sect,buf))
+					== NULL)
+					value=NULL;
+
+				sprintf(buf,"%s_min",type);
+				min=(int)CONF_get_number(req_conf,attr_sect,buf);
+
+				sprintf(buf,"%s_max",type);
+				max=(int)CONF_get_number(req_conf,attr_sect,buf);
+
+				if (!add_attribute_object(req->req_info->attributes,
+					v->value,def,value,nid,min,max))
+					return 0;
+				}
+			}
+		}
+	else
+		{
+		BIO_printf(bio_err,"No template, please set one up.\n");
+		return 0;
+		}
+
+	return 1;
+
+	}
+
+static int auto_info(X509_REQ *req, STACK_OF(CONF_VALUE) *dn_sk,
+			STACK_OF(CONF_VALUE) *attr_sk, int attribs)
+	{
+	int i;
+	char *p,*q;
+	char *type;
+	CONF_VALUE *v;
+	X509_NAME *subj;
+
+	subj = X509_REQ_get_subject_name(req);
+
+	for (i = 0; i < sk_CONF_VALUE_num(dn_sk); i++)
+		{
+		v=sk_CONF_VALUE_value(dn_sk,i);
+		p=q=NULL;
+		type=v->name;
+		/* Skip past any leading X. X: X, etc to allow for
+		 * multiple instances 
+		 */
+		for(p = v->name; *p ; p++) 
+			if ((*p == ':') || (*p == ',') || (*p == '.')) {
+				p++;
+				if(*p) type = p;
+				break;
+			}
+		if (!X509_NAME_add_entry_by_txt(subj,type, MBSTRING_ASC,
+				(unsigned char *) v->value,-1,-1,0)) return 0;
+
+		}
+
+		if (!X509_NAME_entry_count(subj))
+			{
+			BIO_printf(bio_err,"error, no objects specified in config file\n");
+			return 0;
+			}
+#if 0
+		if (attribs)
+			{
+			if ((attr_sk != NULL) && (sk_CONF_VALUE_num(attr_sk) > 0))
+				{
+				BIO_printf(bio_err,"\nPlease enter the following 'extra' attributes\n");
+				BIO_printf(bio_err,"to be sent with your certificate request\n");
+				}
+
+			i= -1;
+start2:			for (;;)
+				{
+				i++;
+				if ((attr_sk == NULL) ||
+					    (sk_CONF_VALUE_num(attr_sk) <= i))
+					break;
+
+				v=sk_CONF_VALUE_value(attr_sk,i);
+				type=v->name;
+				if ((nid=OBJ_txt2nid(type)) == NID_undef)
+					goto start2;
+
+				sprintf(buf,"%s_default",type);
+				if ((def=CONF_get_string(req_conf,attr_sect,buf))
+					== NULL)
+					def="";
+				
+				sprintf(buf,"%s_value",type);
+				if ((value=CONF_get_string(req_conf,attr_sect,buf))
+					== NULL)
+					value=NULL;
+
+				sprintf(buf,"%s_min",type);
+				min=(int)CONF_get_number(req_conf,attr_sect,buf);
+
+				sprintf(buf,"%s_max",type);
+				max=(int)CONF_get_number(req_conf,attr_sect,buf);
+
+				if (!add_attribute_object(ri->attributes,
+					v->value,def,value,nid,min,max))
+					return 0;
+				}
+			}
+		}
+	else
+		{
+		BIO_printf(bio_err,"No template, please set one up.\n");
+		return 0;
+		}
+#endif
+	return 1;
+	}
+
+
 static int add_DN_object(X509_NAME *n, char *text, char *def, char *value,
 	     int nid, int min, int max)
 	{
diff --git a/crypto/asn1/a_mbstr.c b/crypto/asn1/a_mbstr.c
index bc9cb14..ca8d9ea 100644
--- a/crypto/asn1/a_mbstr.c
+++ b/crypto/asn1/a_mbstr.c
@@ -72,54 +72,6 @@
 static int cpy_utf8(unsigned long value, void *arg);
 static int is_printable(unsigned long value);
 
-/* This is the default mask for the mbstring functions: it is designed
- * to be a "safe" DirectoryString. Netscape messenger crashes when it
- * receives a certificate containing a BMPString so by default we don't
- * use them unless we have to.
- */
-
-static long dirstring_mask = B_ASN1_PRINTABLESTRING
-				| B_ASN1_T61STRING | B_ASN1_BMPSTRING;
-
-void ASN1_STRING_set_default_mask(unsigned long mask)
-{
-	dirstring_mask = mask;
-}
-
-unsigned long ASN1_STRING_get_default_mask(void)
-{
-	return dirstring_mask;
-}
-
-/* This function sets the default to various "flavours" of configuration.
- * based on an ASCII string. Currently this is:
- * MASK:XXXX : a numerical mask value.
- * nobmp : Don't use BMPStrings (just Printable, T61).
- * pkix : PKIX recommendation in RFC2459.
- * utf8only : only use UTF8Strings (RFC2459 recommendation for 2004).
- * default:   the default value, Printable, T61, BMP.
- */
-
-int ASN1_STRING_set_default_mask_asc(char *p)
-{
-	unsigned long mask;
-	char *end;
-	if(!strncmp(p, "MASK:", 5)) {
-		if(!p[5]) return 0;
-		mask = strtoul(p + 5, &end, 0);
-		if(*end) return 0;
-	} else if(!strcmp(p, "nobmp"))
-			 mask = B_ASN1_PRINTABLESTRING | B_ASN1_T61STRING;
-	else if(!strcmp(p, "pkix"))
-			mask = B_ASN1_PRINTABLESTRING | B_ASN1_BMPSTRING;
-	else if(!strcmp(p, "utf8only")) mask = B_ASN1_UTF8STRING;
-	else if(!strcmp(p, "default"))
-	    mask = B_ASN1_PRINTABLESTRING | B_ASN1_T61STRING | B_ASN1_BMPSTRING;
-	else return 0;
-	ASN1_STRING_set_default_mask(mask);
-	return 1;
-}
-
 /* These functions take a string in UTF8, ASCII or multibyte form and
  * a mask of permissible ASN1 string types. It then works out the minimal
  * type (using the order Printable < IA5 < T61 < BMP < Universal < UTF8)
@@ -147,7 +99,7 @@
 	char strbuf[32];
 	int (*cpyfunc)(unsigned long,void *) = NULL;
 	if(len == -1) len = strlen((const char *)in);
-	if(!mask) mask = dirstring_mask;
+	if(!mask) mask = DIRSTRING_TYPE;
 
 	/* First do a string check and work out the number of characters */
 	switch(inform) {
diff --git a/crypto/asn1/a_strnid.c b/crypto/asn1/a_strnid.c
index 2f9c09b..a51ae43 100644
--- a/crypto/asn1/a_strnid.c
+++ b/crypto/asn1/a_strnid.c
@@ -68,6 +68,53 @@
 static int sk_table_cmp(ASN1_STRING_TABLE **a, ASN1_STRING_TABLE **b);
 static int table_cmp(ASN1_STRING_TABLE *a, ASN1_STRING_TABLE *b);
 
+
+/* This is the global mask for the mbstring functions: this is use to
+ * mask out certain types (such as BMPString and UTF8String) because
+ * certain software (e.g. Netscape) has problems with them.
+ */
+
+static long global_mask = 0xFFFFFFFFL;
+
+void ASN1_STRING_set_default_mask(unsigned long mask)
+{
+	global_mask = mask;
+}
+
+unsigned long ASN1_STRING_get_default_mask(void)
+{
+	return global_mask;
+}
+
+/* This function sets the default to various "flavours" of configuration.
+ * based on an ASCII string. Currently this is:
+ * MASK:XXXX : a numerical mask value.
+ * nobmp : Don't use BMPStrings (just Printable, T61).
+ * pkix : PKIX recommendation in RFC2459.
+ * utf8only : only use UTF8Strings (RFC2459 recommendation for 2004).
+ * default:   the default value, Printable, T61, BMP.
+ */
+
+int ASN1_STRING_set_default_mask_asc(char *p)
+{
+	unsigned long mask;
+	char *end;
+	if(!strncmp(p, "MASK:", 5)) {
+		if(!p[5]) return 0;
+		mask = strtoul(p + 5, &end, 0);
+		if(*end) return 0;
+	} else if(!strcmp(p, "nombchar"))
+			 mask = ~(B_ASN1_BMPSTRING|B_ASN1_UTF8STRING);
+	else if(!strcmp(p, "pkix"))
+			mask = ~B_ASN1_T61STRING;
+	else if(!strcmp(p, "utf8only")) mask = B_ASN1_UTF8STRING;
+	else if(!strcmp(p, "default"))
+	    mask = 0xFFFFFFFFL;
+	else return 0;
+	ASN1_STRING_set_default_mask(mask);
+	return 1;
+}
+
 /* The following function generates an ASN1_STRING based on limits in a table.
  * Frequently the types and length of an ASN1_STRING are restricted by a 
  * corresponding OID. For example certificates and certificate requests.
@@ -78,12 +125,16 @@
 {
 	ASN1_STRING_TABLE *tbl;
 	ASN1_STRING *str = NULL;
+	unsigned long mask;
 	int ret;
 	if(!out) out = &str;
 	tbl = ASN1_STRING_TABLE_get(nid);
-	if(tbl) ret = ASN1_mbstring_ncopy(out, in, inlen, inform, tbl->mask,
+	if(tbl) {
+		mask = tbl->mask;
+		if(!(tbl->flags & STABLE_NO_MASK)) mask &= global_mask;
+		ret = ASN1_mbstring_ncopy(out, in, inlen, inform, tbl->mask,
 					tbl->minsize, tbl->maxsize);
-	else ret = ASN1_mbstring_copy(out, in, inlen, inform, 0);
+	} else ret = ASN1_mbstring_copy(out, in, inlen, inform, DIRSTRING_TYPE & global_mask);
 	if(ret <= 0) return NULL;
 	return *out;
 }
@@ -105,18 +156,18 @@
 /* This table must be kept in NID order */
 
 static ASN1_STRING_TABLE tbl_standard[] = {
-{NID_commonName,		1, ub_common_name, 0, 0},
-{NID_countryName,		2, 2, B_ASN1_PRINTABLESTRING, 0},
-{NID_localityName,		1, ub_locality_name, 0, 0},
-{NID_stateOrProvinceName,	1, ub_state_name, 0, 0},
-{NID_organizationName,		1, ub_organization_name, 0, 0},
-{NID_organizationalUnitName,	1, ub_organization_unit_name, 0, 0},
-{NID_pkcs9_emailAddress,	1, ub_email_address, B_ASN1_IA5STRING, 0},
-{NID_givenName,			1, ub_name, 0, 0},
-{NID_surname,			1, ub_name, 0, 0},
-{NID_initials,			1, ub_name, 0, 0},
-{NID_name,			1, ub_name, 0, 0},
-{NID_dnQualifier,		-1, -1, B_ASN1_PRINTABLESTRING, 0},
+{NID_commonName,		1, ub_common_name, DIRSTRING_TYPE, 0},
+{NID_countryName,		2, 2, B_ASN1_PRINTABLESTRING, STABLE_NO_MASK},
+{NID_localityName,		1, ub_locality_name, DIRSTRING_TYPE, 0},
+{NID_stateOrProvinceName,	1, ub_state_name, DIRSTRING_TYPE, 0},
+{NID_organizationName,		1, ub_organization_name, DIRSTRING_TYPE, 0},
+{NID_organizationalUnitName,	1, ub_organization_unit_name, DIRSTRING_TYPE, 0},
+{NID_pkcs9_emailAddress,	1, ub_email_address, B_ASN1_IA5STRING, STABLE_NO_MASK},
+{NID_givenName,			1, ub_name, DIRSTRING_TYPE, 0},
+{NID_surname,			1, ub_name, DIRSTRING_TYPE, 0},
+{NID_initials,			1, ub_name, DIRSTRING_TYPE, 0},
+{NID_name,			1, ub_name, DIRSTRING_TYPE, 0},
+{NID_dnQualifier,		-1, -1, B_ASN1_PRINTABLESTRING, STABLE_NO_MASK},
 };
 
 static int sk_table_cmp(ASN1_STRING_TABLE **a, ASN1_STRING_TABLE **b)
diff --git a/crypto/asn1/asn1.h b/crypto/asn1/asn1.h
index c9500a6..aba0b5f 100644
--- a/crypto/asn1/asn1.h
+++ b/crypto/asn1/asn1.h
@@ -212,6 +212,9 @@
 	} ASN1_STRING;
 
 #define STABLE_FLAGS_MALLOC	0x01
+#define STABLE_NO_MASK		0x02
+#define DIRSTRING_TYPE	\
+ (B_ASN1_PRINTABLESTRING|B_ASN1_T61STRING|B_ASN1_BMPSTRING|B_ASN1_UTF8STRING)
 
 typedef struct asn1_string_table_st {
 	int nid;
diff --git a/crypto/x509/Makefile.ssl b/crypto/x509/Makefile.ssl
index 346ffb2..a569c80 100644
--- a/crypto/x509/Makefile.ssl
+++ b/crypto/x509/Makefile.ssl
@@ -25,13 +25,13 @@
 LIBSRC=	x509_def.c x509_d2.c x509_r2x.c x509_cmp.c \
 	x509_obj.c x509_req.c x509spki.c x509_vfy.c \
 	x509_set.c x509rset.c x509_err.c \
-	x509name.c x509_v3.c x509_ext.c \
+	x509name.c x509_v3.c x509_ext.c x509_att.c \
 	x509type.c x509_lu.c x_all.c x509_txt.c \
 	x509_trs.c by_file.c by_dir.c 
 LIBOBJ= x509_def.o x509_d2.o x509_r2x.o x509_cmp.o \
 	x509_obj.o x509_req.o x509spki.o x509_vfy.o \
 	x509_set.o x509rset.o x509_err.o \
-	x509name.o x509_v3.o x509_ext.o \
+	x509name.o x509_v3.o x509_ext.o x509_att.o \
 	x509type.o x509_lu.o x_all.o x509_txt.o \
 	x509_trs.o by_file.o by_dir.o
 
diff --git a/crypto/x509/x509.h b/crypto/x509/x509.h
index 2e6d207..9f5f9a1 100644
--- a/crypto/x509/x509.h
+++ b/crypto/x509/x509.h
@@ -1019,6 +1019,27 @@
 ASN1_OCTET_STRING *X509_EXTENSION_get_data(X509_EXTENSION *ne);
 int		X509_EXTENSION_get_critical(X509_EXTENSION *ex);
 
+
+int X509_get_attr_count(const STACK_OF(X509_ATTRIBUTE) *x);
+int X509_get_attr_by_NID(const STACK_OF(X509_ATTRIBUTE) *x, int nid,
+			  int lastpos);
+int X509_get_attr_by_OBJ(const STACK_OF(X509_ATTRIBUTE) *sk, ASN1_OBJECT *obj,
+			  int lastpos);
+X509_ATTRIBUTE *X509_get_attr(const STACK_OF(X509_ATTRIBUTE) *x, int loc);
+X509_ATTRIBUTE *X509_delete_attr(STACK_OF(X509_ATTRIBUTE) *x, int loc);
+STACK_OF(X509_ATTRIBUTE) *X509_radd_attr(STACK_OF(X509_ATTRIBUTE) **x,
+					 X509_ATTRIBUTE *attr, int loc);
+X509_ATTRIBUTE *X509_ATTRIBUTE_create_by_NID(X509_ATTRIBUTE **attr, int nid,
+	     int atrtype, void *data);
+X509_ATTRIBUTE *X509_ATTRIBUTE_create_by_OBJ(X509_ATTRIBUTE **attr,
+	     ASN1_OBJECT *obj, int atrtype, void *data);
+int X509_ATTRIBUTE_rset_object(X509_ATTRIBUTE *attr, ASN1_OBJECT *obj);
+int X509_ATTRIBUTE_iset_data(X509_ATTRIBUTE *attr, int attrtype, void *data);
+void *X509_ATTRIBUTE_iget_data(X509_ATTRIBUTE *attr, int idx,
+					int atrtype, void *data);
+ASN1_OBJECT *X509_ATTRIBUTE_iget_object(X509_ATTRIBUTE *attr);
+ASN1_TYPE *X509_ATTRIBUTE_type_iget(X509_ATTRIBUTE *attr, int idx);
+
 int		X509_verify_cert(X509_STORE_CTX *ctx);
 
 /* lookup a cert from a X509 STACK */
@@ -1082,6 +1103,11 @@
 #define X509_F_NETSCAPE_SPKI_B64_DECODE			 129
 #define X509_F_NETSCAPE_SPKI_B64_ENCODE			 130
 #define X509_F_X509V3_ADD_EXT				 104
+#define X509_F_X509_ADD_ATTR				 135
+#define X509_F_X509_ATTRIBUTE_CREATE_BY_NID		 136
+#define X509_F_X509_ATTRIBUTE_CREATE_BY_OBJ		 137
+#define X509_F_X509_ATTRIBUTE_IGET_DATA			 139
+#define X509_F_X509_ATTRIBUTE_ISET_DATA			 138
 #define X509_F_X509_CHECK_PRIVATE_KEY			 128
 #define X509_F_X509_EXTENSION_CREATE_BY_NID		 108
 #define X509_F_X509_EXTENSION_CREATE_BY_OBJ		 109
@@ -1130,6 +1156,7 @@
 #define X509_R_UNKNOWN_TRUST_ID				 120
 #define X509_R_UNSUPPORTED_ALGORITHM			 111
 #define X509_R_WRONG_LOOKUP_TYPE			 112
+#define X509_R_WRONG_TYPE				 122
 
 #ifdef  __cplusplus
 }
diff --git a/crypto/x509/x509_att.c b/crypto/x509/x509_att.c
new file mode 100644
index 0000000..f755cce
--- /dev/null
+++ b/crypto/x509/x509_att.c
@@ -0,0 +1,258 @@
+/* crypto/x509/x509_att.c */
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ * 
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ * 
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from 
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ * 
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * 
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ */
+
+#include <stdio.h>
+#include <openssl/stack.h>
+#include "cryptlib.h"
+#include <openssl/asn1.h>
+#include <openssl/objects.h>
+#include <openssl/evp.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+
+int X509_get_attr_count(const STACK_OF(X509_ATTRIBUTE) *x)
+{
+	if (!x) return 0;
+	return(sk_X509_ATTRIBUTE_num(x));
+}
+
+int X509_get_attr_by_NID(const STACK_OF(X509_ATTRIBUTE) *x, int nid,
+			  int lastpos)
+{
+	ASN1_OBJECT *obj;
+
+	obj=OBJ_nid2obj(nid);
+	if (obj == NULL) return(-2);
+	return(X509_get_attr_by_OBJ(x,obj,lastpos));
+}
+
+int X509_get_attr_by_OBJ(const STACK_OF(X509_ATTRIBUTE) *sk, ASN1_OBJECT *obj,
+			  int lastpos)
+{
+	int n;
+	X509_ATTRIBUTE *ex;
+
+	if (sk == NULL) return(-1);
+	lastpos++;
+	if (lastpos < 0)
+		lastpos=0;
+	n=sk_X509_ATTRIBUTE_num(sk);
+	for ( ; lastpos < n; lastpos++)
+		{
+		ex=sk_X509_ATTRIBUTE_value(sk,lastpos);
+		if (OBJ_cmp(ex->object,obj) == 0)
+			return(lastpos);
+		}
+	return(-1);
+}
+
+X509_ATTRIBUTE *X509_get_attr(const STACK_OF(X509_ATTRIBUTE) *x, int loc)
+{
+	if (x == NULL || sk_X509_ATTRIBUTE_num(x) <= loc || loc < 0)
+		return NULL;
+	else
+		return sk_X509_ATTRIBUTE_value(x,loc);
+}
+
+X509_ATTRIBUTE *X509_delete_attr(STACK_OF(X509_ATTRIBUTE) *x, int loc)
+{
+	X509_ATTRIBUTE *ret;
+
+	if (x == NULL || sk_X509_ATTRIBUTE_num(x) <= loc || loc < 0)
+		return(NULL);
+	ret=sk_X509_ATTRIBUTE_delete(x,loc);
+	return(ret);
+}
+
+STACK_OF(X509_ATTRIBUTE) *X509_radd_attr(STACK_OF(X509_ATTRIBUTE) **x,
+					 X509_ATTRIBUTE *attr, int loc)
+{
+	X509_ATTRIBUTE *new_attr=NULL;
+	int n;
+	STACK_OF(X509_ATTRIBUTE) *sk=NULL;
+
+	if ((x != NULL) && (*x == NULL))
+		{
+		if ((sk=sk_X509_ATTRIBUTE_new_null()) == NULL)
+			goto err;
+		}
+	else
+		sk= *x;
+
+	n=sk_X509_ATTRIBUTE_num(sk);
+	if (loc > n) loc=n;
+	else if (loc < 0) loc=n;
+
+	if ((new_attr=X509_ATTRIBUTE_dup(attr)) == NULL)
+		goto err2;
+	if (!sk_X509_ATTRIBUTE_insert(sk,new_attr,loc))
+		goto err;
+	if ((x != NULL) && (*x == NULL))
+		*x=sk;
+	return(sk);
+err:
+	X509err(X509_F_X509_ADD_ATTR,ERR_R_MALLOC_FAILURE);
+err2:
+	if (new_attr != NULL) X509_ATTRIBUTE_free(new_attr);
+	if (sk != NULL) sk_X509_ATTRIBUTE_free(sk);
+	return(NULL);
+}
+
+X509_ATTRIBUTE *X509_ATTRIBUTE_create_by_NID(X509_ATTRIBUTE **attr, int nid,
+	     int atrtype, void *data)
+{
+	ASN1_OBJECT *obj;
+	X509_ATTRIBUTE *ret;
+
+	obj=OBJ_nid2obj(nid);
+	if (obj == NULL)
+		{
+		X509err(X509_F_X509_ATTRIBUTE_CREATE_BY_NID,X509_R_UNKNOWN_NID);
+		return(NULL);
+		}
+	ret=X509_ATTRIBUTE_create_by_OBJ(attr,obj,atrtype,data);
+	if (ret == NULL) ASN1_OBJECT_free(obj);
+	return(ret);
+}
+
+X509_ATTRIBUTE *X509_ATTRIBUTE_create_by_OBJ(X509_ATTRIBUTE **attr,
+	     ASN1_OBJECT *obj, int atrtype, void *data)
+{
+	X509_ATTRIBUTE *ret;
+
+	if ((attr == NULL) || (*attr == NULL))
+		{
+		if ((ret=X509_ATTRIBUTE_new()) == NULL)
+			{
+			X509err(X509_F_X509_ATTRIBUTE_CREATE_BY_OBJ,ERR_R_MALLOC_FAILURE);
+			return(NULL);
+			}
+		}
+	else
+		ret= *attr;
+
+	if (!X509_ATTRIBUTE_rset_object(ret,obj))
+		goto err;
+	if (!X509_ATTRIBUTE_iset_data(ret,atrtype,data))
+		goto err;
+	
+	if ((attr != NULL) && (*attr == NULL)) *attr=ret;
+	return(ret);
+err:
+	if ((attr == NULL) || (ret != *attr))
+		X509_ATTRIBUTE_free(ret);
+	return(NULL);
+}
+
+int X509_ATTRIBUTE_rset_object(X509_ATTRIBUTE *attr, ASN1_OBJECT *obj)
+{
+	if ((attr == NULL) || (obj == NULL))
+		return(0);
+	ASN1_OBJECT_free(attr->object);
+	attr->object=OBJ_dup(obj);
+	return(1);
+}
+
+int X509_ATTRIBUTE_iset_data(X509_ATTRIBUTE *attr, int attrtype, void *data)
+{
+	ASN1_TYPE *ttmp;
+	if (!attr) return 0;
+	if(!(attr->value.set = sk_ASN1_TYPE_new_null())) goto err;
+	if(!(ttmp = ASN1_TYPE_new())) goto err;
+	if(!sk_ASN1_TYPE_push(attr->value.set, ttmp)) goto err;
+	attr->set = 1;
+	ASN1_TYPE_set(ttmp, attrtype, data);
+	return 1;
+	err:
+	X509err(X509_F_X509_ATTRIBUTE_ISET_DATA, ERR_R_MALLOC_FAILURE);
+	return 0;
+}
+
+int X509_ATTRIBUTE_count(X509_ATTRIBUTE *attr)
+{
+	if(attr->set) return sk_ASN1_TYPE_num(attr->value.set);
+	if(attr->value.single) return 1;
+	return 0;
+}
+
+ASN1_OBJECT *X509_ATTRIBUTE_iget_object(X509_ATTRIBUTE *attr)
+{
+	if (attr == NULL) return(NULL);
+	return(attr->object);
+}
+
+void *X509_ATTRIBUTE_iget_data(X509_ATTRIBUTE *attr, int idx,
+					int atrtype, void *data)
+{
+	ASN1_TYPE *ttmp;
+	ttmp = X509_ATTRIBUTE_type_iget(attr, idx);
+	if(!ttmp) return NULL;
+	if(atrtype != ASN1_TYPE_get(ttmp)){
+		X509err(X509_F_X509_ATTRIBUTE_IGET_DATA, X509_R_WRONG_TYPE);
+		return NULL;
+	}
+	return ttmp->value.ptr;
+}
+
+ASN1_TYPE *X509_ATTRIBUTE_type_iget(X509_ATTRIBUTE *attr, int idx)
+{
+	if (attr == NULL) return(NULL);
+	if(idx >= X509_ATTRIBUTE_count(attr)) return NULL;
+	if(attr->set) return sk_ASN1_TYPE_value(attr->value.set, idx);
+	else return attr->value.single;
+}
diff --git a/crypto/x509/x509_err.c b/crypto/x509/x509_err.c
index 6c85ddb..326aeca3 100644
--- a/crypto/x509/x509_err.c
+++ b/crypto/x509/x509_err.c
@@ -72,6 +72,11 @@
 {ERR_PACK(0,X509_F_NETSCAPE_SPKI_B64_DECODE,0),	"NETSCAPE_SPKI_b64_decode"},
 {ERR_PACK(0,X509_F_NETSCAPE_SPKI_B64_ENCODE,0),	"NETSCAPE_SPKI_b64_encode"},
 {ERR_PACK(0,X509_F_X509V3_ADD_EXT,0),	"X509v3_add_ext"},
+{ERR_PACK(0,X509_F_X509_ADD_ATTR,0),	"X509_ADD_ATTR"},
+{ERR_PACK(0,X509_F_X509_ATTRIBUTE_CREATE_BY_NID,0),	"X509_ATTRIBUTE_create_by_NID"},
+{ERR_PACK(0,X509_F_X509_ATTRIBUTE_CREATE_BY_OBJ,0),	"X509_ATTRIBUTE_create_by_OBJ"},
+{ERR_PACK(0,X509_F_X509_ATTRIBUTE_IGET_DATA,0),	"X509_ATTRIBUTE_iget_data"},
+{ERR_PACK(0,X509_F_X509_ATTRIBUTE_ISET_DATA,0),	"X509_ATTRIBUTE_iset_data"},
 {ERR_PACK(0,X509_F_X509_CHECK_PRIVATE_KEY,0),	"X509_check_private_key"},
 {ERR_PACK(0,X509_F_X509_EXTENSION_CREATE_BY_NID,0),	"X509_EXTENSION_create_by_NID"},
 {ERR_PACK(0,X509_F_X509_EXTENSION_CREATE_BY_OBJ,0),	"X509_EXTENSION_create_by_OBJ"},
@@ -123,6 +128,7 @@
 {X509_R_UNKNOWN_TRUST_ID                 ,"unknown trust id"},
 {X509_R_UNSUPPORTED_ALGORITHM            ,"unsupported algorithm"},
 {X509_R_WRONG_LOOKUP_TYPE                ,"wrong lookup type"},
+{X509_R_WRONG_TYPE                       ,"wrong type"},
 {0,NULL}
 	};
 
diff --git a/doc/man/ca.pod b/doc/man/ca.pod
index fbc4cba..1f2d6f2 100644
--- a/doc/man/ca.pod
+++ b/doc/man/ca.pod
@@ -448,7 +448,7 @@
 
 It is not possible to certify two certificates with the same DN: this
 is a side effect of how the text database is indexed and it cannot easily
-be fixed without introducing other problems. Netscape apparently can use
+be fixed without introducing other problems. Some S/MIME clients can use
 two certificates with the same DN for separate signing and encryption
 keys.
 
diff --git a/doc/man/req.pod b/doc/man/req.pod
index 0211530..7dbd5d5 100644
--- a/doc/man/req.pod
+++ b/doc/man/req.pod
@@ -146,7 +146,7 @@
 
 this specifies the message digest to sign the request with. This
 overrides the digest algorithm specified in the configuration file.
-This option is ignore for DSA requests: they always use SHA1.
+This option is ignored for DSA requests: they always use SHA1.
 
 =item B<-config filename>
 
@@ -203,6 +203,13 @@
 
 =over 4
 
+=item B<input_password output_password>
+
+The passwords for the input private key file (if present) and
+the output private key file (if one will be created). The
+command line options B<passin>, B<envpassin>, B<passout> and
+B<envpassout> override the configuration file values.
+
 =item B<default_bits>
 
 This specifies the default key size in bits. If not specified then
@@ -234,11 +241,11 @@
 This specifies a filename in which random number seed information is
 placed and read from. It is used for private key generation.
 
-=item B<encrypt_rsa_key|encrypt_key>
+=item B<encrypt_key>
 
 If this is set to B<no> then if a private key is generated it is
 B<not> encrypted. This is equivalent to the B<-nodes> command line
-option.
+option. For compatability B<encrypt_rsai_key> is an equivalent option.
 
 =item B<default_md>
 
@@ -246,19 +253,19 @@
 include B<md5 sha1 mdc2>. If not present then MD5 is used. This
 option can be overridden on the command line.
 
-=item B<dirstring_type>
+=item B<string_mask>
 
-This option specifies which string types are permissible in a
-B<DirectoryString>. Most users will not need to change this option.
+This option masks out the use of certain string types in certain
+fields. Most users will not need to change this option.
 
 It can be set to several values B<default> which is also the default
 option uses PrintableStrings, T61Strings and BMPStrings if the 
 B<pkix> value is used then only PrintableStrings and BMPStrings will
 be used. This follows the PKIX recommendation in RFC2459. If the
 B<utf8only> option is used then only UTF8Strings will be used: this
-is the PKIX recommendation in RFC2459 after 2003. Finally the B<nobmp>
+is the PKIX recommendation in RFC2459 after 2003. Finally the B<nombstr>
 option just uses PrintableStrings and T61Strings: certain software has
-problems with BMPStrings.
+problems with BMPStrings and UTF8Strings: in particular Netscape.
 
 =item B<req_extensions>
 
@@ -277,8 +284,8 @@
 this specifies the section containing any request attributes: its format
 is the same as B<distinguished_name> described below. Typically these
 may contain the challengePassword or unstructuredName types. They are
-currently ignored by OpenSSLs request signing utilities but some CAs might want
-want them.
+currently ignored by OpenSSLs request signing utilities but some CAs
+might want them.
 
 =item B<distinguished_name>