Hello,

Would it be possible to add generic engine support to OpenSSH? One use in particular would be to support TCP forwarding for secure mail server connections and similar applications. This would permit an administrator to configure in an arbitrary external engine to establish a secure RSA-based tunnel. OpenSSH would need no information built into it to accomodate any particular engine.

One approach to this would be that taken by Stunnel (http://www.stunnel.org/). Stunnel first loads the internal engine 'dynamic'. It then uses ENGINE_load_builtin_engines(), but I think ENGINE_load_dynamic() would be more efficient. Next a series of ENGINE_ctrl_cmd_string() and other engine(1) calls are used to locate, name, load and initialize an external engine. IMHO, they did a very nice job of achieving a highly versatile implementation. It was easy to configure in the particular engine I use.

Another approach would be to take advantage of the OpenSSL config(5) configuration file mechanism. I don't know of any good examples of this out on the web, but I coded up an implementation that works fine with OpenSSL 0.9.7 or 0.9.8 distributions on HP-UX, see following. I should also mention the Stunnel approach works fine with 0.9.7 or 0.9.8 OpenSSL. I only coded this for sshd, but it would be very much the same for ssh. This takes fewer config file directives and is less difficult to implement than the stunnel approach, but does introduce the additional OpenSSL configuration file.

Regards, Rich
HP-UX Security

========================================
Code changes for an engine-enabled sshd.
========================================
In this example, CONF_modules_load_file(3) is used to implement engine loading into sshd. This will require three additional configuration parameters to be parsed, as well as the introduction of an OpenSSL CONF-formatted file for the directives, see config(5) for format details.

================================================
Added to sshd_config for generic engine loading:
------------------------------------------------
# Identify the RSA key to be loaded by the engine
EngineHostKey /usr/local/etc/enginekeyblob

# Give location of the OpenSSL CONF file
EngineConfigFile /usr/local/etc/server.cnf

# Identify section for engine directives within the engine CONF file
EngineConfigStanza server_conf
----------------------------------------------------------

===============================================
A typical OpenSSL CONF file for loading an external engine
---------------------------------------------
# Filename: /usr/local/etc/server.cnf

server_conf = server_def

[ server_def ]
engines = server_engines

[ server_engines ]
myengine = engine_section

[ engine_section ]
# Rename the engine to whatever, if necessary
engine_id = myengine

# external engine location
dynamic_path = /opt/openssl/lib/hpux32/engines/libmyengine.so

#default_algorithms = RAND,RSA
default_algorithms = ALL

# Load and initialize the engine
init = 1
----------------------------------

===========================================
Add to 'struct ServerOptions' in servconf.h:
--------------------------------------------
50a68,71
> int engineindex; /*engine index in host_key_files */
> char *engconffile; /*engine config information */
> char *engconfstanza; /* engine config stanza */
>

--------------------------------------------

===================
Add to servconf.c:
--------------------------------------------
74a67,69
> options->engineindex = -1;
> options->engconffile = NULL;
> options->engconfstanza = NULL;


188a193,200
> if (options->engineindex != -1) {
> /* Set defaults for configuring an OpenSSL engine */
> if (options->engconffile == NULL)
> options->engconffile = _PATH_OPENSSL_CONFIG;
>
> if (options->engconfstanza == NULL)
> options->engconfstanza = OPENSSL_STANZA;
> }


409a471,473
> { "enginehostkey", sEngineHostKey, SSHCFG_GLOBAL },
> { "engineconfigfile", sEngineConfigFile, SSHCFG_GLOBAL },
> { "engineconfigstanza", sEngineConfigStanza, SSHCFG_GLOBAL },


915a995,1021
> case sEngineHostKey:
> if (options->engineindex != -1) {
> fatal("%s line %d: One engine key allowed",
> filename, linenum);
> }
> options->engineindex = options->num_host_key_files;
> intptr = &options->num_host_key_files;
> if (*intptr >= MAX_HOSTKEYS)
> fatal("%s line %d: too many keys (max %d).",
> filename, linenum, MAX_HOSTKEYS);
> charptr = &options->host_key_files[*intptr];
> goto parse_filename;
>
> case sEngineConfigFile:
> /* default set in fill_default_server_options */
> charptr = &options->engconffile;
> goto parse_filename;
>
> case sEngineConfigStanza:
> /* default set in fill_default_server_options */
> charptr = &options->engconfstanza;
> arg = strdelim(&cp);
> if (!arg || *arg == '\0')
> fatal("%s line %d: missing stanza",
> filename, linenum);
> break;
>

---------------------------------------------

=============================================
key_load_engine_private() added to authfile.c:
---------------------------------------------
48a49,50
> #include
> #include

611a614,679
> return prv;
> }
>
> /* Arguments passphrase and commentp are not used */
> Key *
> key_load_engine_private(char *engkey, const char *conffile,
> const char* stanza, const char *passphrase, char **commentp)
> {
> ENGINE *eng = NULL;
> EVP_PKEY *pk = NULL;
> Key *prv = NULL;
> char *name = "";
>
> // Load the OpenSSL internal engine called 'dynamic'
> ENGINE_load_dynamic();
>
> // Add the OpenSSL ENGINE configuration module
> OPENSSL_load_builtin_modules();
>
> // Identify the file and stanza for engine directives
> if (CONF_modules_load_file(conffile, stanza, 0) <= 0) {
> ERR_print_errors_fp(stderr);
> error("Auto configuration failed");
> goto finish;
> }
>
> // Fetch the external engine handle
> eng = ENGINE_get_last();
> if (!eng) {
> /* the engine isn't available */
> ERR_print_errors_fp(stderr);
> error("ENGINE_get_last failed.");
> goto finish;
> }
>
> // Fetch and store the private key through the engine
> pk = ENGINE_load_private_key(eng, engkey, NULL, (void *)passphrase);
> if (pk == NULL) {
> ERR_print_errors_fp(stderr);
> debug("ENGINE_load_private_key failed");
> (void)ERR_get_error();
> goto finish;
> } else if (pk->type == EVP_PKEY_RSA) {
> prv = key_new(KEY_UNSPEC);
> prv->rsa = EVP_PKEY_get1_RSA(pk);
> prv->type = KEY_RSA;
> name = "rsa w/o comment";
> #ifdef DEBUG_PK
> RSA_print_fp(stderr, prv->rsa, 8);
> #endif
> if (RSA_blinding_on(prv->rsa, NULL) != 1) {
> ERR_print_errors_fp(stderr);
> error("key_load_eng_prv: RSA_blinding failed");
> key_free(prv);
> prv = NULL;
> }
> } else {
> error("key_load_engine_private: key type wrong");
> }
>
> finish: if (pk != NULL)
> EVP_PKEY_free(pk);
> if (prv != NULL && commentp)
> *commentp = xstrdup(name);
> debug("key_load_engine_private() done: type %s",
> prv ? key_type(prv) : "");

-------------------------------------------------

==================================================
Call to key_load_engine_private() added to sshd.c:
--------------------------------------------------
1521a1546,1551
> if (i == options.engineindex) {
> /* Multiple RSA2 keys allowed, but only one used */
> key = key_load_engine_private(options.host_key_files[i],
> options.engconffile, options.engconfstanza, NULL, NULL);
> debug("engine key load attempted, index: #%d", i);
> } else {

1522a1553
> }

-------------------------------------------------
_______________________________________________
openssh-unix-dev mailing list
openssh-unix-dev@mindrot.org
https://lists.mindrot.org/mailman/li...enssh-unix-dev