mirror of https://github.com/openssl/openssl.git
				
				
				
			Add copy_extensions option to 'ca' utility.
This commit is contained in:
		
							parent
							
								
									e890dcdb19
								
							
						
					
					
						commit
						791bd0cd2b
					
				
							
								
								
									
										4
									
								
								CHANGES
								
								
								
								
							
							
						
						
									
										4
									
								
								CHANGES
								
								
								
								
							|  | @ -3,6 +3,10 @@ | |||
| 
 | ||||
|  Changes between 0.9.6 and 0.9.7  [xx XXX 2000] | ||||
| 
 | ||||
|   *) Add a 'copy_extensions' option to the 'ca' utility. This copies | ||||
|      extensions from a certificate request to the certificate. | ||||
|      [Steve Henson] | ||||
| 
 | ||||
|   *) Allow multiple 'certopt' and 'nameopt' options to be separated | ||||
|      by commas. Add 'namopt' and 'certopt' options to the 'ca' config | ||||
|      file: this allows the display of the certificate about to be | ||||
|  |  | |||
							
								
								
									
										56
									
								
								apps/apps.c
								
								
								
								
							
							
						
						
									
										56
									
								
								apps/apps.c
								
								
								
								
							|  | @ -809,6 +809,62 @@ int set_name_ex(unsigned long *flags, const char *arg) | |||
| 	return set_multi_opts(flags, arg, ex_tbl); | ||||
| } | ||||
| 
 | ||||
| int set_ext_copy(int *copy_type, const char *arg) | ||||
| { | ||||
| 	if (!strcasecmp(arg, "none")) | ||||
| 		*copy_type = EXT_COPY_NONE; | ||||
| 	else if (!strcasecmp(arg, "copy")) | ||||
| 		*copy_type = EXT_COPY_ADD; | ||||
| 	else if (!strcasecmp(arg, "copyall")) | ||||
| 		*copy_type = EXT_COPY_ALL; | ||||
| 	else | ||||
| 		return 0; | ||||
| 	return 1; | ||||
| } | ||||
| 
 | ||||
| int copy_extensions(X509 *x, X509_REQ *req, int copy_type) | ||||
| { | ||||
| 	STACK_OF(X509_EXTENSION) *exts = NULL; | ||||
| 	X509_EXTENSION *ext, *tmpext; | ||||
| 	ASN1_OBJECT *obj; | ||||
| 	int i, idx, ret = 0; | ||||
| 	if (!x || !req || (copy_type == EXT_COPY_NONE)) | ||||
| 		return 1; | ||||
| 	exts = X509_REQ_get_extensions(req); | ||||
| 
 | ||||
| 	for(i = 0; i < sk_X509_EXTENSION_num(exts); i++) { | ||||
| 		ext = sk_X509_EXTENSION_value(exts, i); | ||||
| 		obj = X509_EXTENSION_get_object(ext); | ||||
| 		idx = X509_get_ext_by_OBJ(x, obj, -1); | ||||
| 		/* Does extension exist? */ | ||||
| 		if (idx != -1) { | ||||
| 			/* If normal copy don't override existing extension */ | ||||
| 			if (copy_type == EXT_COPY_ADD) | ||||
| 				continue; | ||||
| 			/* Delete all extensions of same type */ | ||||
| 			do { | ||||
| 				tmpext = X509_get_ext(x, idx); | ||||
| 				X509_delete_ext(x, idx); | ||||
| 				X509_EXTENSION_free(tmpext); | ||||
| 				idx = X509_get_ext_by_OBJ(x, obj, -1); | ||||
| 			} while (idx != -1); | ||||
| 		} | ||||
| 		if (!X509_add_ext(x, ext, -1)) | ||||
| 			goto end; | ||||
| 	} | ||||
| 
 | ||||
| 	ret = 1; | ||||
| 
 | ||||
| 	end: | ||||
| 
 | ||||
| 	sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 		 | ||||
| 		 | ||||
| 			 | ||||
| 
 | ||||
| static int set_multi_opts(unsigned long *flags, const char *arg, const NAME_EX_TBL *in_tbl) | ||||
| { | ||||
| 	STACK_OF(CONF_VALUE) *vals; | ||||
|  |  | |||
|  | @ -153,6 +153,8 @@ void print_name(BIO *out, char *title, X509_NAME *nm, unsigned long lflags); | |||
| #endif | ||||
| int set_cert_ex(unsigned long *flags, const char *arg); | ||||
| int set_name_ex(unsigned long *flags, const char *arg); | ||||
| int set_ext_copy(int *copy_type, const char *arg); | ||||
| int copy_extensions(X509 *x, X509_REQ *req, int copy_type); | ||||
| int app_passwd(BIO *err, char *arg1, char *arg2, char **pass1, char **pass2); | ||||
| int add_oid_section(BIO *err, LHASH *conf); | ||||
| X509 *load_cert(BIO *err, char *file, int format); | ||||
|  | @ -170,6 +172,10 @@ X509_STORE *setup_verify(BIO *bp, char *CAfile, char *CApath); | |||
| #define FORMAT_SMIME    6 | ||||
| #define FORMAT_ENGINE   7 | ||||
| 
 | ||||
| #define EXT_COPY_NONE	0 | ||||
| #define EXT_COPY_ADD	1 | ||||
| #define EXT_COPY_ALL	2 | ||||
| 
 | ||||
| #define NETSCAPE_CERT_HDR	"certificate" | ||||
| 
 | ||||
| #define APP_PASS_LEN	1024 | ||||
|  |  | |||
							
								
								
									
										84
									
								
								apps/ca.c
								
								
								
								
							
							
						
						
									
										84
									
								
								apps/ca.c
								
								
								
								
							|  | @ -134,6 +134,7 @@ | |||
| #define ENV_MSIE_HACK		"msie_hack" | ||||
| #define ENV_NAMEOPT		"name_opt" | ||||
| #define ENV_CERTOPT		"cert_opt" | ||||
| #define ENV_EXTCOPY		"copy_extensions" | ||||
| 
 | ||||
| #define ENV_DATABASE		"database" | ||||
| 
 | ||||
|  | @ -303,7 +304,7 @@ int MAIN(int argc, char **argv) | |||
| 	int notext=0; | ||||
| 	unsigned long nameopt = 0, certopt = 0; | ||||
| 	int default_op = 1; | ||||
| 	int ext_copy = 0; | ||||
| 	int ext_copy = EXT_COPY_NONE; | ||||
| 	X509 *x509=NULL; | ||||
| 	X509 *x=NULL; | ||||
| 	BIO *in=NULL,*out=NULL,*Sout=NULL,*Cout=NULL; | ||||
|  | @ -798,6 +799,18 @@ bad: | |||
| 	else | ||||
| 		ERR_clear_error(); | ||||
| 
 | ||||
| 	f=CONF_get_string(conf,section,ENV_EXTCOPY); | ||||
| 
 | ||||
| 	if (f) | ||||
| 		{ | ||||
| 		if (!set_ext_copy(&ext_copy, f)) | ||||
| 			{ | ||||
| 			BIO_printf(bio_err, "Invalid extension copy option: \"%s\"\n", f); | ||||
| 			goto err; | ||||
| 			} | ||||
| 		} | ||||
| 	else | ||||
| 		ERR_clear_error(); | ||||
| 
 | ||||
| 	/*****************************************************************/ | ||||
| 	/* lookup where to write new certificates */ | ||||
|  | @ -1944,40 +1957,6 @@ static int do_body(X509 **xret, EVP_PKEY *pkey, X509 *x509, const EVP_MD *dgst, | |||
| 
 | ||||
| 		if (default_op) | ||||
| 			old_entry_print(bio_err, obj, str); | ||||
| #if 0 | ||||
| 		j=i2a_ASN1_OBJECT(bio_err,obj); | ||||
| 		pbuf=buf; | ||||
| 		for (j=22-j; j>0; j--) | ||||
| 			*(pbuf++)=' '; | ||||
| 		*(pbuf++)=':'; | ||||
| 		*(pbuf++)='\0'; | ||||
| 		BIO_puts(bio_err,buf); | ||||
| 
 | ||||
| 		if (str->type == V_ASN1_PRINTABLESTRING) | ||||
| 			BIO_printf(bio_err,"PRINTABLE:'"); | ||||
| 		else if (str->type == V_ASN1_T61STRING) | ||||
| 			BIO_printf(bio_err,"T61STRING:'"); | ||||
| 		else if (str->type == V_ASN1_IA5STRING) | ||||
| 			BIO_printf(bio_err,"IA5STRING:'"); | ||||
| 		else if (str->type == V_ASN1_UNIVERSALSTRING) | ||||
| 			BIO_printf(bio_err,"UNIVERSALSTRING:'"); | ||||
| 		else | ||||
| 			BIO_printf(bio_err,"ASN.1 %2d:'",str->type); | ||||
| 			 | ||||
| 		p=(char *)str->data; | ||||
| 		for (j=str->length; j>0; j--) | ||||
| 			{ | ||||
| 			if ((*p >= ' ') && (*p <= '~')) | ||||
| 				BIO_printf(bio_err,"%c",*p); | ||||
| 			else if (*p & 0x80) | ||||
| 				BIO_printf(bio_err,"\\0x%02X",*p); | ||||
| 			else if ((unsigned char)*p == 0xf7) | ||||
| 				BIO_printf(bio_err,"^?"); | ||||
| 			else	BIO_printf(bio_err,"^%c",*p+'@'); | ||||
| 			p++; | ||||
| 			} | ||||
| 		BIO_printf(bio_err,"'\n"); | ||||
| #endif | ||||
| 		} | ||||
| 
 | ||||
| 	/* Ok, now we check the 'policy' stuff. */ | ||||
|  | @ -2171,7 +2150,6 @@ again2: | |||
| 	if (!X509_set_issuer_name(ret,X509_get_subject_name(x509))) | ||||
| 		goto err; | ||||
| 
 | ||||
| 	BIO_printf(bio_err,"Certificate is to be certified until "); | ||||
| 	if (strcmp(startdate,"today") == 0) | ||||
| 		X509_gmtime_adj(X509_get_notBefore(ret),0); | ||||
| 	else ASN1_UTCTIME_set_string(X509_get_notBefore(ret),startdate); | ||||
|  | @ -2180,10 +2158,6 @@ again2: | |||
| 		X509_gmtime_adj(X509_get_notAfter(ret),(long)60*60*24*days); | ||||
| 	else ASN1_UTCTIME_set_string(X509_get_notAfter(ret),enddate); | ||||
| 
 | ||||
| 	ASN1_UTCTIME_print(bio_err,X509_get_notAfter(ret)); | ||||
| 	if (days) BIO_printf(bio_err," (%d days)",days); | ||||
| 	BIO_printf(bio_err, "\n"); | ||||
| 
 | ||||
| 	if (!X509_set_subject_name(ret,subject)) goto err; | ||||
| 
 | ||||
| 	pktmp=X509_REQ_get_pubkey(req); | ||||
|  | @ -2251,16 +2225,32 @@ again2: | |||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 	/* Copy extensions from request (if any) */ | ||||
| 
 | ||||
| 	if (!copy_extensions(ret, req, ext_copy)) | ||||
| 		{ | ||||
| 		BIO_printf(bio_err, "ERROR: adding extensions from request\n"); | ||||
| 		ERR_print_errors(bio_err); | ||||
| 		goto err; | ||||
| 		} | ||||
| 
 | ||||
| 
 | ||||
| 	if (!default_op) | ||||
| 		{ | ||||
| 		BIO_printf(bio_err, "Certificate Details:\n"); | ||||
| 		/* Never print signature details because signature not present */ | ||||
| 		certopt |= X509_FLAG_NO_SIGDUMP | X509_FLAG_NO_SIGNAME; | ||||
| 		X509_print_ex(bio_err, ret, nameopt, certopt);  | ||||
| 		} | ||||
| 
 | ||||
| 	BIO_printf(bio_err,"Certificate is to be certified until "); | ||||
| 	ASN1_UTCTIME_print(bio_err,X509_get_notAfter(ret)); | ||||
| 	if (days) BIO_printf(bio_err," (%d days)",days); | ||||
| 	BIO_printf(bio_err, "\n"); | ||||
| 
 | ||||
| 	if (!batch) | ||||
| 		{ | ||||
| 		if (!default_op) | ||||
| 			{ | ||||
| 			BIO_printf(bio_err, "Certificate Details:\n"); | ||||
| 			/* Never print signature details because signature not present */ | ||||
| 			certopt |= X509_FLAG_NO_SIGDUMP | X509_FLAG_NO_SIGNAME; | ||||
| 			X509_print_ex(bio_err, ret, nameopt, certopt);  | ||||
| 			} | ||||
| 
 | ||||
| 		BIO_printf(bio_err,"Sign the certificate? [y/n]:"); | ||||
| 		(void)BIO_flush(bio_err); | ||||
| 		buf[0]='\0'; | ||||
|  |  | |||
|  | @ -53,6 +53,9 @@ x509_extensions	= usr_cert		# The extentions to add to the cert | |||
| name_opt 	= ca_default		# Subject Name options | ||||
| cert_opt 	= ca_default		# Certificate field options | ||||
| 
 | ||||
| # Extension copying option: use with caution. | ||||
| # copy_extensions = copy | ||||
| 
 | ||||
| # Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs | ||||
| # so this is commented out by default to leave a V1 CRL. | ||||
| # crl_extensions	= crl_ext | ||||
|  |  | |||
|  | @ -334,6 +334,20 @@ OpenSSL is used. Use of the old format is B<strongly> discouraged because | |||
| it only displays fields mentioned in the B<policy> section, mishandles | ||||
| multicharacter string types and does not display extensions. | ||||
| 
 | ||||
| =item B<copy_extensions> | ||||
| 
 | ||||
| determines how extensions in certificate requests should be handled. | ||||
| If set to B<none> or this option is not present then extensions are | ||||
| ignored and not copied to the certificate. If set to B<copy> then any | ||||
| extensions present in the request that are not already present are copied | ||||
| to the certificate. If set to B<copyall> then all extensions in the | ||||
| request are copied to the certificate: if the extension is already present | ||||
| in the certificate it is deleted first. See the B<WARNINGS> section before | ||||
| using this option. | ||||
| 
 | ||||
| The main use of this option is to allow a certificate request to supply | ||||
| values for certain extensions such as subjectAltName. | ||||
| 
 | ||||
| =back | ||||
| 
 | ||||
| =head1 POLICY FORMAT | ||||
|  | @ -426,6 +440,7 @@ A sample configuration file with the relevant sections for B<ca>: | |||
| 
 | ||||
|  nameopt	= default_ca		# Subject name display option | ||||
|  certopt	= default_ca		# Certificate display option | ||||
|  copy_extensions = none			# Don't copy extensions from request | ||||
| 
 | ||||
|  [ policy_any ] | ||||
|  countryName            = supplied | ||||
|  | @ -491,10 +506,6 @@ The use of an in memory text database can cause problems when large | |||
| numbers of certificates are present because, as the name implies | ||||
| the database has to be kept in memory. | ||||
| 
 | ||||
| Certificate request extensions are ignored: some kind of "policy" should | ||||
| be included to use certain static extensions and certain extensions | ||||
| from the request. | ||||
| 
 | ||||
| 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. Some S/MIME clients can use | ||||
|  | @ -513,6 +524,30 @@ The behaviour should be more friendly and configurable. | |||
| Cancelling some commands by refusing to certify a certificate can | ||||
| create an empty file. | ||||
| 
 | ||||
| =head1 WARNINGS | ||||
| 
 | ||||
| The B<copy_extensions> option should be used with caution. If care is | ||||
| not taken then it can be a security risk. For example if a certificate | ||||
| request contains a basicConstraints extension with CA:TRUE and the | ||||
| B<copy_extensions> value is set to B<copyall> and the user does not spot | ||||
| this when the certificate is displayed then this will hand the requestor | ||||
| a valid CA certificate. | ||||
| 
 | ||||
| This situation can be avoided by setting B<copy_extensions> to B<copy> | ||||
| and including basicConstraints with CA:FALSE in the configuration file. | ||||
| Then if the request contains a basicConstraints extension it will be | ||||
| ignored. | ||||
| 
 | ||||
| It is advisable to also include values for other extensions such | ||||
| as B<keyUsage> to prevent a request supplying its own values. | ||||
| 
 | ||||
| Additional restrictions can be placed on the CA certificate itself. | ||||
| For example if the CA certificate has: | ||||
| 
 | ||||
|  basicConstraints = CA:TRUE, pathlen:0 | ||||
| 
 | ||||
| then even if a certificate is issued with CA:TRUE it will not be valid. | ||||
| 
 | ||||
| =head1 SEE ALSO | ||||
| 
 | ||||
| L<req(1)|req(1)>, L<spkac(1)|spkac(1)>, L<x509(1)|x509(1)>, L<CA.pl(1)|CA.pl(1)>, | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue