Fix bugs introduced by the rebase 0.10.4

- Move loglevel closer to openssh loglevel
- Add openssh config feature of +,-,^ for algorithm lists
- Fix memory leaks of bignum
- Prevent multiple expansion of escape characters
- Resolves: rhbz#2132407, rhbz#2137839, rhbz#2144795, rhbz#2136824

Signed-off-by: Norbert Pocs <npocs@redhat.com>
This commit is contained in:
Norbert Pocs 2022-11-30 17:43:29 +01:00
parent d9f5f2e6ff
commit 2a1987b511
5 changed files with 7532 additions and 1 deletions

View File

@ -1,6 +1,6 @@
Name: libssh Name: libssh
Version: 0.10.4 Version: 0.10.4
Release: 3%{?dist} Release: 4%{?dist}
Summary: A library implementing the SSH protocol Summary: A library implementing the SSH protocol
License: LGPLv2+ License: LGPLv2+
URL: http://www.libssh.org URL: http://www.libssh.org
@ -41,6 +41,10 @@ Provides: libssh_threads.so.4
Patch1: coverity_scan.patch Patch1: coverity_scan.patch
Patch2: pkcs11_test_fix.patch Patch2: pkcs11_test_fix.patch
Patch3: loglevel.patch
Patch4: plus_sign.patch
Patch5: memory_leak.patch
Patch6: options_apply.patch
%description %description
The ssh library was designed to be used by programmers needing a working SSH The ssh library was designed to be used by programmers needing a working SSH
@ -133,6 +137,13 @@ popd
%attr(0644,root,root) %config(noreplace) %{_sysconfdir}/libssh/libssh_server.config %attr(0644,root,root) %config(noreplace) %{_sysconfdir}/libssh/libssh_server.config
%changelog %changelog
* Wed Nov 30 2022 Norbert Pocs <npocs@redhat.com> - 0.10.4-4
- Move loglevel closer to openssh loglevel
- Add openssh config feature of +,-,^ for algorithm lists
- Fix memory leaks of bignum
- Prevent multiple expansion of escape characters
- Resolves: rhbz#2132407, rhbz#2137839, rhbz#2144795, rhbz#2136824
* Tue Oct 4 2022 Norbert Pocs <npocs@redhat.com> - 0.10.4-3 * Tue Oct 4 2022 Norbert Pocs <npocs@redhat.com> - 0.10.4-3
- Enable pkcs11 support - Enable pkcs11 support
- Fix broken libsofthsm path on i686 - Fix broken libsofthsm path on i686

4385
loglevel.patch Normal file

File diff suppressed because it is too large Load Diff

514
memory_leak.patch Normal file
View File

@ -0,0 +1,514 @@
From 8795d8912c8a83aaf900c0260e252a35f64eb200 Mon Sep 17 00:00:00 2001
From: Norbert Pocs <npocs@redhat.com>
Date: Fri, 18 Nov 2022 17:22:46 +0100
Subject: [PATCH] Fix memory leaks of bignums when openssl >= 3.0
The openssl 3.0 support has introduced some memory leaks at key build as
OSSL_PARAM_BLD_push_BN duplicates the bignum and does not save the pointer
itself.
Signed-off-by: Norbert Pocs <npocs@redhat.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
---
include/libssh/dh.h | 2 +-
src/dh_crypto.c | 28 ++---
src/pki_crypto.c | 262 ++++++++++++++++++++++++--------------------
3 files changed, 151 insertions(+), 141 deletions(-)
diff --git a/include/libssh/dh.h b/include/libssh/dh.h
index 353dc233..9b9bb472 100644
--- a/include/libssh/dh.h
+++ b/include/libssh/dh.h
@@ -53,7 +53,7 @@ int ssh_dh_keypair_get_keys(struct dh_ctx *ctx, int peer,
bignum *priv, bignum *pub);
#endif /* OPENSSL_VERSION_NUMBER */
int ssh_dh_keypair_set_keys(struct dh_ctx *ctx, int peer,
- const bignum priv, const bignum pub);
+ bignum priv, bignum pub);
int ssh_dh_compute_shared_secret(struct dh_ctx *ctx, int local, int remote,
bignum *dest);
diff --git a/src/dh_crypto.c b/src/dh_crypto.c
index a847c6a2..b578ddec 100644
--- a/src/dh_crypto.c
+++ b/src/dh_crypto.c
@@ -154,12 +154,9 @@ int ssh_dh_keypair_get_keys(struct dh_ctx *ctx, int peer,
#endif /* OPENSSL_VERSION_NUMBER */
int ssh_dh_keypair_set_keys(struct dh_ctx *ctx, int peer,
- const bignum priv, const bignum pub)
+ bignum priv, bignum pub)
{
-#if OPENSSL_VERSION_NUMBER < 0x30000000L
- bignum priv_key = NULL;
- bignum pub_key = NULL;
-#else
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
int rc;
OSSL_PARAM *params = NULL, *out_params = NULL, *merged_params = NULL;
OSSL_PARAM_BLD *param_bld = NULL;
@@ -172,7 +169,11 @@ int ssh_dh_keypair_set_keys(struct dh_ctx *ctx, int peer,
return SSH_ERROR;
}
-#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ (void)DH_set0_key(ctx->keypair[peer], pub, priv);
+
+ return SSH_OK;
+#else
rc = EVP_PKEY_todata(ctx->keypair[peer], EVP_PKEY_KEYPAIR, &out_params);
if (rc != 1) {
return SSH_ERROR;
@@ -195,35 +196,22 @@ int ssh_dh_keypair_set_keys(struct dh_ctx *ctx, int peer,
rc = SSH_ERROR;
goto out;
}
-#endif /* OPENSSL_VERSION_NUMBER */
if (priv) {
-#if OPENSSL_VERSION_NUMBER < 0x30000000L
- priv_key = priv;
-#else
rc = OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_PRIV_KEY, priv);
if (rc != 1) {
rc = SSH_ERROR;
goto out;
}
-#endif /* OPENSSL_VERSION_NUMBER */
}
if (pub) {
-#if OPENSSL_VERSION_NUMBER < 0x30000000L
- pub_key = pub;
-#else
rc = OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_PUB_KEY, pub);
if (rc != 1) {
rc = SSH_ERROR;
goto out;
}
-#endif /* OPENSSL_VERSION_NUMBER */
}
-#if OPENSSL_VERSION_NUMBER < 0x30000000L
- (void)DH_set0_key(ctx->keypair[peer], pub_key, priv_key);
- return SSH_OK;
-#else
params = OSSL_PARAM_BLD_to_param(param_bld);
if (params == NULL) {
rc = SSH_ERROR;
@@ -248,6 +236,8 @@ int ssh_dh_keypair_set_keys(struct dh_ctx *ctx, int peer,
rc = SSH_OK;
out:
+ bignum_safe_free(priv);
+ bignum_safe_free(pub);
EVP_PKEY_CTX_free(evp_ctx);
OSSL_PARAM_free(out_params);
OSSL_PARAM_free(params);
diff --git a/src/pki_crypto.c b/src/pki_crypto.c
index 0a5003da..d3359e2d 100644
--- a/src/pki_crypto.c
+++ b/src/pki_crypto.c
@@ -1492,18 +1492,18 @@ int pki_privkey_build_dss(ssh_key key,
ssh_string privkey)
{
int rc;
-#if OPENSSL_VERSION_NUMBER < 0x30000000L
BIGNUM *bp, *bq, *bg, *bpub_key, *bpriv_key;
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ OSSL_PARAM_BLD *param_bld = OSSL_PARAM_BLD_new();
+ if (param_bld == NULL) {
+ return SSH_ERROR;
+ }
#else
- const BIGNUM *pb, *qb, *gb, *pubb, *privb;
- OSSL_PARAM_BLD *param_bld;
-#endif /* OPENSSL_VERSION_NUMBER */
-
-#if OPENSSL_VERSION_NUMBER < 0x30000000L
key->dsa = DSA_new();
if (key->dsa == NULL) {
return SSH_ERROR;
}
+#endif /* OPENSSL_VERSION_NUMBER */
bp = ssh_make_string_bn(p);
bq = ssh_make_string_bn(q);
@@ -1512,9 +1512,11 @@ int pki_privkey_build_dss(ssh_key key,
bpriv_key = ssh_make_string_bn(privkey);
if (bp == NULL || bq == NULL ||
bg == NULL || bpub_key == NULL) {
+ rc = SSH_ERROR;
goto fail;
}
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
/* Memory management of bp, qq and bg is transferred to DSA object */
rc = DSA_set0_pqg(key->dsa, bp, bq, bg);
if (rc == 0) {
@@ -1532,39 +1534,43 @@ fail:
DSA_free(key->dsa);
return SSH_ERROR;
#else
- param_bld = OSSL_PARAM_BLD_new();
- if (param_bld == NULL)
- goto err;
-
- pb = ssh_make_string_bn(p);
- qb = ssh_make_string_bn(q);
- gb = ssh_make_string_bn(g);
- pubb = ssh_make_string_bn(pubkey);
- privb = ssh_make_string_bn(privkey);
-
- rc = OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_FFC_P, pb);
- if (rc != 1)
- goto err;
- rc = OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_FFC_Q, qb);
- if (rc != 1)
- goto err;
- rc = OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_FFC_G, gb);
- if (rc != 1)
- goto err;
- rc = OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_PUB_KEY, pubb);
- if (rc != 1)
- goto err;
- rc = OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_PRIV_KEY, privb);
- if (rc != 1)
- goto err;
+ rc = OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_FFC_P, bp);
+ if (rc != 1) {
+ rc = SSH_ERROR;
+ goto fail;
+ }
+ rc = OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_FFC_Q, bq);
+ if (rc != 1) {
+ rc = SSH_ERROR;
+ goto fail;
+ }
+ rc = OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_FFC_G, bg);
+ if (rc != 1) {
+ rc = SSH_ERROR;
+ goto fail;
+ }
+ rc = OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_PUB_KEY, bpub_key);
+ if (rc != 1) {
+ rc = SSH_ERROR;
+ goto fail;
+ }
+ rc = OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_PRIV_KEY, bpriv_key);
+ if (rc != 1) {
+ rc = SSH_ERROR;
+ goto fail;
+ }
rc = evp_build_pkey("DSA", param_bld, &(key->key), EVP_PKEY_KEYPAIR);
+
+fail:
OSSL_PARAM_BLD_free(param_bld);
+ bignum_safe_free(bp);
+ bignum_safe_free(bq);
+ bignum_safe_free(bg);
+ bignum_safe_free(bpub_key);
+ bignum_safe_free(bpriv_key);
return rc;
-err:
- OSSL_PARAM_BLD_free(param_bld);
- return -1;
#endif /* OPENSSL_VERSION_NUMBER */
}
@@ -1574,18 +1580,18 @@ int pki_pubkey_build_dss(ssh_key key,
ssh_string g,
ssh_string pubkey) {
int rc;
-#if OPENSSL_VERSION_NUMBER < 0x30000000L
BIGNUM *bp = NULL, *bq = NULL, *bg = NULL, *bpub_key = NULL;
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ OSSL_PARAM_BLD *param_bld = OSSL_PARAM_BLD_new();
+ if (param_bld == NULL) {
+ return SSH_ERROR;
+ }
#else
- const BIGNUM *pb, *qb, *gb, *pubb;
- OSSL_PARAM_BLD *param_bld;
-#endif /* OPENSSL_VERSION_NUMBER */
-
-#if OPENSSL_VERSION_NUMBER < 0x30000000L
key->dsa = DSA_new();
if (key->dsa == NULL) {
return SSH_ERROR;
}
+#endif /* OPENSSL_VERSION_NUMBER */
bp = ssh_make_string_bn(p);
bq = ssh_make_string_bn(q);
@@ -1593,9 +1599,11 @@ int pki_pubkey_build_dss(ssh_key key,
bpub_key = ssh_make_string_bn(pubkey);
if (bp == NULL || bq == NULL ||
bg == NULL || bpub_key == NULL) {
+ rc = SSH_ERROR;
goto fail;
}
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
/* Memory management of bp, bq and bg is transferred to DSA object */
rc = DSA_set0_pqg(key->dsa, bp, bq, bg);
if (rc == 0) {
@@ -1613,35 +1621,37 @@ fail:
DSA_free(key->dsa);
return SSH_ERROR;
#else
- param_bld = OSSL_PARAM_BLD_new();
- if (param_bld == NULL)
- goto err;
-
- pb = ssh_make_string_bn(p);
- qb = ssh_make_string_bn(q);
- gb = ssh_make_string_bn(g);
- pubb = ssh_make_string_bn(pubkey);
-
- rc = OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_FFC_P, pb);
- if (rc != 1)
- goto err;
- rc = OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_FFC_Q, qb);
- if (rc != 1)
- goto err;
- rc = OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_FFC_G, gb);
- if (rc != 1)
- goto err;
- rc = OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_PUB_KEY, pubb);
- if (rc != 1)
- goto err;
+ rc = OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_FFC_P, bp);
+ if (rc != 1) {
+ rc = SSH_ERROR;
+ goto fail;
+ }
+ rc = OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_FFC_Q, bq);
+ if (rc != 1) {
+ rc = SSH_ERROR;
+ goto fail;
+ }
+ rc = OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_FFC_G, bg);
+ if (rc != 1) {
+ rc = SSH_ERROR;
+ goto fail;
+ }
+ rc = OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_PUB_KEY, bpub_key);
+ if (rc != 1) {
+ rc = SSH_ERROR;
+ goto fail;
+ }
rc = evp_build_pkey("DSA", param_bld, &(key->key), EVP_PKEY_PUBLIC_KEY);
+
+fail:
OSSL_PARAM_BLD_free(param_bld);
+ bignum_safe_free(bp);
+ bignum_safe_free(bq);
+ bignum_safe_free(bg);
+ bignum_safe_free(bpub_key);
return rc;
-err:
- OSSL_PARAM_BLD_free(param_bld);
- return -1;
#endif /* OPENSSL_VERSION_NUMBER */
}
@@ -1654,18 +1664,18 @@ int pki_privkey_build_rsa(ssh_key key,
ssh_string q)
{
int rc;
-#if OPENSSL_VERSION_NUMBER < 0x30000000L
BIGNUM *be, *bn, *bd/*, *biqmp*/, *bp, *bq;
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ OSSL_PARAM_BLD *param_bld = OSSL_PARAM_BLD_new();
+ if (param_bld == NULL) {
+ return SSH_ERROR;
+ }
#else
- const BIGNUM *nb, *eb, *db, *pb, *qb;
- OSSL_PARAM_BLD *param_bld;
-#endif /* OPENSSL_VERSION_NUMBER */
-
-#if OPENSSL_VERSION_NUMBER < 0x30000000L
key->rsa = RSA_new();
if (key->rsa == NULL) {
return SSH_ERROR;
}
+#endif /* OPENSSL_VERSION_NUMBER */
bn = ssh_make_string_bn(n);
be = ssh_make_string_bn(e);
@@ -1675,9 +1685,11 @@ int pki_privkey_build_rsa(ssh_key key,
bq = ssh_make_string_bn(q);
if (be == NULL || bn == NULL || bd == NULL ||
/*biqmp == NULL ||*/ bp == NULL || bq == NULL) {
+ rc = SSH_ERROR;
goto fail;
}
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
/* Memory management of be, bn and bd is transferred to RSA object */
rc = RSA_set0_key(key->rsa, bn, be, bd);
if (rc == 0) {
@@ -1702,41 +1714,49 @@ fail:
RSA_free(key->rsa);
return SSH_ERROR;
#else
- param_bld = OSSL_PARAM_BLD_new();
- if (param_bld == NULL)
- goto err;
-
- nb = ssh_make_string_bn(n);
- eb = ssh_make_string_bn(e);
- db = ssh_make_string_bn(d);
- pb = ssh_make_string_bn(p);
- qb = ssh_make_string_bn(q);
-
- rc = OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_RSA_N, nb);
- if (rc != 1)
- goto err;
- rc = OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_RSA_E, eb);
- if (rc != 1)
- goto err;
- rc = OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_RSA_D, db);
- if (rc != 1)
- goto err;
+ rc = OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_RSA_N, bn);
+ if (rc != 1) {
+ rc = SSH_ERROR;
+ goto fail;
+ }
+ rc = OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_RSA_E, be);
+ if (rc != 1) {
+ rc = SSH_ERROR;
+ goto fail;
+ }
+ rc = OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_RSA_D, bd);
+ if (rc != 1) {
+ rc = SSH_ERROR;
+ goto fail;
+ }
rc = evp_build_pkey("RSA", param_bld, &(key->key), EVP_PKEY_KEYPAIR);
- OSSL_PARAM_BLD_free(param_bld);
+ if (rc != SSH_OK) {
+ rc = SSH_ERROR;
+ goto fail;
+ }
- rc = EVP_PKEY_set_bn_param(key->key, OSSL_PKEY_PARAM_RSA_FACTOR1, pb);
- if (rc != 1)
- goto err;
+ rc = EVP_PKEY_set_bn_param(key->key, OSSL_PKEY_PARAM_RSA_FACTOR1, bp);
+ if (rc != 1) {
+ rc = SSH_ERROR;
+ goto fail;
+ }
- rc = EVP_PKEY_set_bn_param(key->key, OSSL_PKEY_PARAM_RSA_FACTOR2, qb);
- if (rc != 1)
- goto err;
+ rc = EVP_PKEY_set_bn_param(key->key, OSSL_PKEY_PARAM_RSA_FACTOR2, bq);
+ if (rc != 1) {
+ rc = SSH_ERROR;
+ goto fail;
+ }
- return rc;
-err:
+fail:
OSSL_PARAM_BLD_free(param_bld);
- return -1;
+ bignum_safe_free(bn);
+ bignum_safe_free(be);
+ bignum_safe_free(bd);
+ bignum_safe_free(bp);
+ bignum_safe_free(bq);
+
+ return rc;
#endif /* OPENSSL_VERSION_NUMBER */
}
@@ -1744,25 +1764,27 @@ int pki_pubkey_build_rsa(ssh_key key,
ssh_string e,
ssh_string n) {
int rc;
-#if OPENSSL_VERSION_NUMBER < 0x30000000L
BIGNUM *be = NULL, *bn = NULL;
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ OSSL_PARAM_BLD *param_bld = OSSL_PARAM_BLD_new();
+ if (param_bld == NULL) {
+ return SSH_ERROR;
+ }
#else
- const BIGNUM *eb, *nb;
- OSSL_PARAM_BLD *param_bld;
-#endif /* OPENSSL_VERSION_NUMBER */
-
-#if OPENSSL_VERSION_NUMBER < 0x30000000L
key->rsa = RSA_new();
if (key->rsa == NULL) {
return SSH_ERROR;
}
+#endif /* OPENSSL_VERSION_NUMBER */
be = ssh_make_string_bn(e);
bn = ssh_make_string_bn(n);
if (be == NULL || bn == NULL) {
+ rc = SSH_ERROR;
goto fail;
}
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
/* Memory management of bn and be is transferred to RSA object */
rc = RSA_set0_key(key->rsa, bn, be, NULL);
if (rc == 0) {
@@ -1774,27 +1796,25 @@ fail:
RSA_free(key->rsa);
return SSH_ERROR;
#else
- nb = ssh_make_string_bn(n);
- eb = ssh_make_string_bn(e);
-
- param_bld = OSSL_PARAM_BLD_new();
- if (param_bld == NULL)
- goto err;
-
- rc = OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_RSA_N, nb);
- if (rc != 1)
- goto err;
- rc = OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_RSA_E, eb);
- if (rc != 1)
- goto err;
+ rc = OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_RSA_N, bn);
+ if (rc != 1) {
+ rc = SSH_ERROR;
+ goto fail;
+ }
+ rc = OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_RSA_E, be);
+ if (rc != 1) {
+ rc = SSH_ERROR;
+ goto fail;
+ }
rc = evp_build_pkey("RSA", param_bld, &(key->key), EVP_PKEY_PUBLIC_KEY);
+
+fail:
OSSL_PARAM_BLD_free(param_bld);
+ bignum_safe_free(bn);
+ bignum_safe_free(be);
return rc;
-err:
- OSSL_PARAM_BLD_free(param_bld);
- return -1;
#endif /* OPENSSL_VERSION_NUMBER */
}
--
2.38.1

968
options_apply.patch Normal file
View File

@ -0,0 +1,968 @@
From e7dd88167b68cbee7c603e8cd5fbb96ef3040c85 Mon Sep 17 00:00:00 2001
From: Norbert Pocs <npocs@redhat.com>
Date: Wed, 16 Nov 2022 10:40:38 +0100
Subject: [PATCH 1/5] Add a placehohlder for non-expanded identities
Expanding a string twice could lead to unwanted behaviour.
This solution creates a ssh_list (`opts.identites_non_exp`) to store the strings
before expansion and by using ssh_apply it moves the string to the
`opts.identities`. This way the expanded strings are separated.
Signed-off-by: Norbert Pocs <npocs@redhat.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
---
include/libssh/session.h | 1 +
src/options.c | 86 +++++++++++++++++++++++++---------------
src/session.c | 23 +++++++++--
3 files changed, 75 insertions(+), 35 deletions(-)
diff --git a/include/libssh/session.h b/include/libssh/session.h
index d3e5787c..e22b0d67 100644
--- a/include/libssh/session.h
+++ b/include/libssh/session.h
@@ -209,6 +209,7 @@ struct ssh_session_struct {
#endif
struct {
struct ssh_list *identity;
+ struct ssh_list *identity_non_exp;
char *username;
char *host;
char *bindaddr; /* bind the client to an ip addr */
diff --git a/src/options.c b/src/options.c
index 56e09c65..bb085384 100644
--- a/src/options.c
+++ b/src/options.c
@@ -52,7 +52,7 @@
* @brief Duplicate the options of a session structure.
*
* If you make several sessions with the same options this is useful. You
- * cannot use twice the same option structure in ssh_session_connect.
+ * cannot use twice the same option structure in ssh_connect.
*
* @param src The session to use to copy the options.
*
@@ -61,13 +61,14 @@
*
* @returns 0 on success, -1 on error with errno set.
*
- * @see ssh_session_connect()
+ * @see ssh_connect()
* @see ssh_free()
*/
int ssh_options_copy(ssh_session src, ssh_session *dest)
{
ssh_session new;
struct ssh_iterator *it = NULL;
+ struct ssh_list *list = NULL;
char *id = NULL;
int i;
@@ -105,14 +106,15 @@ int ssh_options_copy(ssh_session src, ssh_session *dest)
}
/* Remove the default identities */
- for (id = ssh_list_pop_head(char *, new->opts.identity);
+ for (id = ssh_list_pop_head(char *, new->opts.identity_non_exp);
id != NULL;
- id = ssh_list_pop_head(char *, new->opts.identity)) {
+ id = ssh_list_pop_head(char *, new->opts.identity_non_exp)) {
SAFE_FREE(id);
}
/* Copy the new identities from the source list */
- if (src->opts.identity != NULL) {
- it = ssh_list_get_iterator(src->opts.identity);
+ list = new->opts.identity_non_exp;
+ it = ssh_list_get_iterator(src->opts.identity_non_exp);
+ for (i = 0; i < 2; i++) {
while (it) {
int rc;
@@ -122,7 +124,7 @@ int ssh_options_copy(ssh_session src, ssh_session *dest)
return -1;
}
- rc = ssh_list_append(new->opts.identity, id);
+ rc = ssh_list_append(list, id);
if (rc < 0) {
free(id);
ssh_free(new);
@@ -130,6 +132,10 @@ int ssh_options_copy(ssh_session src, ssh_session *dest)
}
it = it->next;
}
+
+ /* copy the identity list if there is any already */
+ list = new->opts.identity;
+ it = ssh_list_get_iterator(src->opts.identity);
}
if (src->opts.sshdir != NULL) {
@@ -331,7 +337,7 @@ int ssh_options_set_algo(ssh_session session,
* Add a new identity file (const char *, format string) to
* the identity list.\n
* \n
- * By default identity, id_dsa and id_rsa are checked.\n
+ * By default id_rsa, id_ecdsa and id_ed25519 files are used.\n
* \n
* The identity used to authenticate with public key will be
* prepended to the list.
@@ -700,7 +706,11 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
if (q == NULL) {
return -1;
}
- rc = ssh_list_prepend(session->opts.identity, q);
+ if (session->opts.exp_flags & SSH_OPT_EXP_FLAG_IDENTITY) {
+ rc = ssh_list_append(session->opts.identity_non_exp, q);
+ } else {
+ rc = ssh_list_prepend(session->opts.identity_non_exp, q);
+ }
if (rc < 0) {
free(q);
return -1;
@@ -1202,7 +1212,7 @@ int ssh_options_get_port(ssh_session session, unsigned int* port_target) {
* - SSH_OPTIONS_IDENTITY:
* Get the first identity file name (const char *).\n
* \n
- * By default identity, id_dsa and id_rsa are checked.
+ * By default id_rsa, id_ecdsa and id_ed25519 files are used.
*
* - SSH_OPTIONS_PROXYCOMMAND:
* Get the proxycommand necessary to log into the
@@ -1246,7 +1256,11 @@ int ssh_options_get(ssh_session session, enum ssh_options_e type, char** value)
break;
}
case SSH_OPTIONS_IDENTITY: {
- struct ssh_iterator *it = ssh_list_get_iterator(session->opts.identity);
+ struct ssh_iterator *it;
+ it = ssh_list_get_iterator(session->opts.identity);
+ if (it == NULL) {
+ it = ssh_list_get_iterator(session->opts.identity_non_exp);
+ }
if (it == NULL) {
return SSH_ERROR;
}
@@ -1541,7 +1555,6 @@ out:
int ssh_options_apply(ssh_session session)
{
- struct ssh_iterator *it;
char *tmp;
int rc;
@@ -1586,15 +1599,17 @@ int ssh_options_apply(ssh_session session)
size_t plen = strlen(session->opts.ProxyCommand) +
5 /* strlen("exec ") */;
- p = malloc(plen + 1 /* \0 */);
- if (p == NULL) {
- return -1;
- }
+ if (strncmp(session->opts.ProxyCommand, "exec ", 5) != 0) {
+ p = malloc(plen + 1 /* \0 */);
+ if (p == NULL) {
+ return -1;
+ }
- rc = snprintf(p, plen + 1, "exec %s", session->opts.ProxyCommand);
- if ((size_t)rc != plen) {
- free(p);
- return -1;
+ rc = snprintf(p, plen + 1, "exec %s", session->opts.ProxyCommand);
+ if ((size_t)rc != plen) {
+ free(p);
+ return -1;
+ }
}
tmp = ssh_path_expand_escape(session, p);
@@ -1606,24 +1621,33 @@ int ssh_options_apply(ssh_session session)
session->opts.ProxyCommand = tmp;
}
- for (it = ssh_list_get_iterator(session->opts.identity);
- it != NULL;
- it = it->next) {
- char *id = (char *) it->data;
- if (strncmp(id, "pkcs11:", 6) == 0) {
+ for (tmp = ssh_list_pop_head(char *, session->opts.identity_non_exp);
+ tmp != NULL;
+ tmp = ssh_list_pop_head(char *, session->opts.identity_non_exp)) {
+ char *id = tmp;
+ if (strncmp(id, "pkcs11:", 6) != 0) {
/* PKCS#11 URIs are using percent-encoding so we can not mix
* it with ssh expansion of ssh escape characters.
- * Skip these identities now, before we will have PKCS#11 support
*/
- continue;
+ tmp = ssh_path_expand_escape(session, id);
+ if (tmp == NULL) {
+ return -1;
+ }
+ free(id);
}
- tmp = ssh_path_expand_escape(session, id);
- if (tmp == NULL) {
+
+ /* use append to keep the order at first call and use prepend
+ * to put anything that comes on the nth calls to the beginning */
+ if (session->opts.exp_flags & SSH_OPT_EXP_FLAG_IDENTITY) {
+ rc = ssh_list_prepend(session->opts.identity, tmp);
+ } else {
+ rc = ssh_list_append(session->opts.identity, tmp);
+ }
+ if (rc != SSH_OK) {
return -1;
}
- free(id);
- it->data = tmp;
}
+ session->opts.exp_flags |= SSH_OPT_EXP_FLAG_IDENTITY;
return 0;
}
diff --git a/src/session.c b/src/session.c
index 64e54957..34a492e4 100644
--- a/src/session.c
+++ b/src/session.c
@@ -118,13 +118,17 @@ ssh_session ssh_new(void)
if (session->opts.identity == NULL) {
goto err;
}
+ session->opts.identity_non_exp = ssh_list_new();
+ if (session->opts.identity_non_exp == NULL) {
+ goto err;
+ }
id = strdup("%d/id_ed25519");
if (id == NULL) {
goto err;
}
- rc = ssh_list_append(session->opts.identity, id);
+ rc = ssh_list_append(session->opts.identity_non_exp, id);
if (rc == SSH_ERROR) {
goto err;
}
@@ -134,7 +138,7 @@ ssh_session ssh_new(void)
if (id == NULL) {
goto err;
}
- rc = ssh_list_append(session->opts.identity, id);
+ rc = ssh_list_append(session->opts.identity_non_exp, id);
if (rc == SSH_ERROR) {
goto err;
}
@@ -144,7 +148,7 @@ ssh_session ssh_new(void)
if (id == NULL) {
goto err;
}
- rc = ssh_list_append(session->opts.identity, id);
+ rc = ssh_list_append(session->opts.identity_non_exp, id);
if (rc == SSH_ERROR) {
goto err;
}
@@ -154,7 +158,7 @@ ssh_session ssh_new(void)
if (id == NULL) {
goto err;
}
- rc = ssh_list_append(session->opts.identity, id);
+ rc = ssh_list_append(session->opts.identity_non_exp, id);
if (rc == SSH_ERROR) {
goto err;
}
@@ -284,6 +288,17 @@ void ssh_free(ssh_session session)
ssh_list_free(session->opts.identity);
}
+ if (session->opts.identity_non_exp) {
+ char *id;
+
+ for (id = ssh_list_pop_head(char *, session->opts.identity_non_exp);
+ id != NULL;
+ id = ssh_list_pop_head(char *, session->opts.identity_non_exp)) {
+ SAFE_FREE(id);
+ }
+ ssh_list_free(session->opts.identity_non_exp);
+ }
+
while ((b = ssh_list_pop_head(struct ssh_buffer_struct *,
session->out_queue)) != NULL) {
SSH_BUFFER_FREE(b);
--
2.38.1
From 364b4102d3056832d22753c73b37eabce50a6161 Mon Sep 17 00:00:00 2001
From: Norbert Pocs <npocs@redhat.com>
Date: Wed, 16 Nov 2022 11:03:30 +0100
Subject: [PATCH 2/5] tests: Use opts.identites_non_exp not opts.identities
The configuration of identities are first saved to `opts.identities_non_exp`,
then moved to `opts.identities` after calling ssh_options_apply and expanding
the identity strings. These tests are testing against the proper configuration
Signed-off-by: Norbert Pocs <npocs@redhat.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
---
tests/client/torture_auth.c | 114 +++++++++++++++++++++++++++++
tests/client/torture_auth_pkcs11.c | 2 +-
tests/unittests/torture_config.c | 3 +-
tests/unittests/torture_options.c | 14 ++--
4 files changed, 124 insertions(+), 9 deletions(-)
diff --git a/tests/client/torture_auth.c b/tests/client/torture_auth.c
index 79dbd4a7..deb095ef 100644
--- a/tests/client/torture_auth.c
+++ b/tests/client/torture_auth.c
@@ -686,6 +686,120 @@ static void torture_auth_agent_nonblocking(void **state) {
assert_ssh_return_code(session, rc);
}
+static void torture_auth_agent_identities_only(void **state)
+{
+ struct torture_state *s = *state;
+ ssh_session session = s->ssh.session;
+ char bob_ssh_key[1024];
+ struct passwd *pwd;
+ int rc;
+ int identities_only = 1;
+ char *id;
+
+ pwd = getpwnam("bob");
+ assert_non_null(pwd);
+
+ snprintf(bob_ssh_key,
+ sizeof(bob_ssh_key),
+ "%s/.ssh/id_rsa",
+ pwd->pw_dir);
+
+ if (!ssh_agent_is_running(session)){
+ print_message("*** Agent not running. Test ignored\n");
+ return;
+ }
+ rc = ssh_options_set(session, SSH_OPTIONS_USER, TORTURE_SSH_USER_ALICE);
+ assert_int_equal(rc, SSH_OK);
+
+ rc = ssh_options_set(session, SSH_OPTIONS_IDENTITY, &identities_only);
+ assert_int_equal(rc, SSH_OK);
+
+ /* Remove the default identities */
+ while ((id = ssh_list_pop_head(char *, session->opts.identity_non_exp)) != NULL) {
+ SAFE_FREE(id);
+ }
+
+ rc = ssh_connect(session);
+ assert_int_equal(rc, SSH_OK);
+
+ rc = ssh_userauth_none(session, NULL);
+ /* This request should return a SSH_REQUEST_DENIED error */
+ if (rc == SSH_ERROR) {
+ assert_int_equal(ssh_get_error_code(session), SSH_REQUEST_DENIED);
+ }
+ rc = ssh_userauth_list(session, NULL);
+ assert_true(rc & SSH_AUTH_METHOD_PUBLICKEY);
+
+ /* Should fail as key is not in config */
+ rc = ssh_userauth_agent(session, NULL);
+ assert_ssh_return_code_equal(session, rc, SSH_AUTH_DENIED);
+
+ /* Re-add a key */
+ rc = ssh_list_append(session->opts.identity, strdup(bob_ssh_key));
+ assert_int_equal(rc, SSH_OK);
+
+ /* Should succeed as key now in config */
+ rc = ssh_userauth_agent(session, NULL);
+ assert_ssh_return_code(session, rc);
+}
+
+static void torture_auth_agent_identities_only_protected(void **state)
+{
+ struct torture_state *s = *state;
+ ssh_session session = s->ssh.session;
+ char bob_ssh_key[1024];
+ struct passwd *pwd;
+ int rc;
+ int identities_only = 1;
+ char *id;
+
+ pwd = getpwnam("bob");
+ assert_non_null(pwd);
+
+ snprintf(bob_ssh_key,
+ sizeof(bob_ssh_key),
+ "%s/.ssh/id_rsa_protected",
+ pwd->pw_dir);
+
+ if (!ssh_agent_is_running(session)){
+ print_message("*** Agent not running. Test ignored\n");
+ return;
+ }
+ rc = ssh_options_set(session, SSH_OPTIONS_USER, TORTURE_SSH_USER_ALICE);
+ assert_int_equal(rc, SSH_OK);
+
+ rc = ssh_options_set(session, SSH_OPTIONS_IDENTITY, &identities_only);
+ assert_int_equal(rc, SSH_OK);
+
+ /* Remove the default identities */
+ while ((id = ssh_list_pop_head(char *, session->opts.identity_non_exp)) != NULL) {
+ SAFE_FREE(id);
+ }
+
+ rc = ssh_connect(session);
+ assert_int_equal(rc, SSH_OK);
+
+ rc = ssh_userauth_none(session, NULL);
+ /* This request should return a SSH_REQUEST_DENIED error */
+ if (rc == SSH_ERROR) {
+ assert_int_equal(ssh_get_error_code(session), SSH_REQUEST_DENIED);
+ }
+ rc = ssh_userauth_list(session, NULL);
+ assert_true(rc & SSH_AUTH_METHOD_PUBLICKEY);
+
+ /* Should fail as key is not in config */
+ rc = ssh_userauth_agent(session, NULL);
+ assert_ssh_return_code_equal(session, rc, SSH_AUTH_DENIED);
+
+ /* Re-add a key */
+ rc = ssh_list_append(session->opts.identity, strdup(bob_ssh_key));
+ assert_int_equal(rc, SSH_OK);
+
+ /* Should succeed as key now in config */
+ rc = ssh_userauth_agent(session, NULL);
+ assert_ssh_return_code(session, rc);
+}
+
static void torture_auth_cert(void **state) {
struct torture_state *s = *state;
ssh_session session = s->ssh.session;
diff --git a/tests/client/torture_auth_pkcs11.c b/tests/client/torture_auth_pkcs11.c
index ee97bff4..e75fea0e 100644
--- a/tests/client/torture_auth_pkcs11.c
+++ b/tests/client/torture_auth_pkcs11.c
@@ -196,7 +196,7 @@ static void torture_auth_autopubkey(void **state, const char *obj_name, const ch
rc = ssh_options_set(session, SSH_OPTIONS_IDENTITY, priv_uri);
assert_int_equal(rc, SSH_OK);
- assert_string_equal(session->opts.identity->root->data, priv_uri);
+ assert_string_equal(session->opts.identity_non_exp->root->data, priv_uri);
rc = ssh_connect(session);
assert_int_equal(rc, SSH_OK);
diff --git a/tests/unittests/torture_config.c b/tests/unittests/torture_config.c
index 354adc2f..100e68f6 100644
--- a/tests/unittests/torture_config.c
+++ b/tests/unittests/torture_config.c
@@ -2078,7 +2078,8 @@ static void torture_config_identity(void **state)
_parse_config(session, NULL, LIBSSH_TESTCONFIG_STRING13, SSH_OK);
- it = ssh_list_get_iterator(session->opts.identity);
+ /* The identities are first added to this temporary list before expanding */
+ it = ssh_list_get_iterator(session->opts.identity_non_exp);
assert_non_null(it);
id = it->data;
/* The identities are prepended to the list so we start with second one */
diff --git a/tests/unittests/torture_options.c b/tests/unittests/torture_options.c
index dc4df383..3be2de8a 100644
--- a/tests/unittests/torture_options.c
+++ b/tests/unittests/torture_options.c
@@ -406,12 +406,12 @@ static void torture_options_set_identity(void **state) {
rc = ssh_options_set(session, SSH_OPTIONS_ADD_IDENTITY, "identity1");
assert_true(rc == 0);
- assert_string_equal(session->opts.identity->root->data, "identity1");
+ assert_string_equal(session->opts.identity_non_exp->root->data, "identity1");
rc = ssh_options_set(session, SSH_OPTIONS_IDENTITY, "identity2");
assert_true(rc == 0);
- assert_string_equal(session->opts.identity->root->data, "identity2");
- assert_string_equal(session->opts.identity->root->next->data, "identity1");
+ assert_string_equal(session->opts.identity_non_exp->root->data, "identity2");
+ assert_string_equal(session->opts.identity_non_exp->root->next->data, "identity1");
}
static void torture_options_get_identity(void **state) {
@@ -429,7 +429,7 @@ static void torture_options_get_identity(void **state) {
rc = ssh_options_set(session, SSH_OPTIONS_IDENTITY, "identity2");
assert_int_equal(rc, SSH_OK);
- assert_string_equal(session->opts.identity->root->data, "identity2");
+ assert_string_equal(session->opts.identity_non_exp->root->data, "identity2");
rc = ssh_options_get(session, SSH_OPTIONS_IDENTITY, &identity);
assert_int_equal(rc, SSH_OK);
assert_non_null(identity);
@@ -867,9 +867,9 @@ static void torture_options_copy(void **state)
assert_non_null(new);
/* Check the identities match */
- it = ssh_list_get_iterator(session->opts.identity);
+ it = ssh_list_get_iterator(session->opts.identity_non_exp);
assert_non_null(it);
- it2 = ssh_list_get_iterator(new->opts.identity);
+ it2 = ssh_list_get_iterator(new->opts.identity_non_exp);
assert_non_null(it2);
while (it != NULL && it2 != NULL) {
assert_string_equal(it->data, it2->data);
@@ -956,7 +956,7 @@ static void torture_options_getopt(void **state)
"aes128-ctr");
assert_string_equal(session->opts.wanted_methods[SSH_CRYPT_S_C],
"aes128-ctr");
- assert_string_equal(session->opts.identity->root->data, "id_rsa");
+ assert_string_equal(session->opts.identity_non_exp->root->data, "id_rsa");
#ifdef WITH_ZLIB
assert_string_equal(session->opts.wanted_methods[SSH_COMP_C_S],
"zlib@openssh.com,zlib,none");
--
2.38.1
From 868e2d7c28b914b3d6f516cfc1e31d79aaddec1c Mon Sep 17 00:00:00 2001
From: Norbert Pocs <npocs@redhat.com>
Date: Wed, 16 Nov 2022 16:51:02 +0100
Subject: [PATCH 3/5] Add flags for escape expand operation
Calling `ssh_options_apply` more times can result in an unwanted behaviour of
expanding the escape characters more times. Adding flags to check if the
expansion was already done on the current string variables.
Signed-off-by: Norbert Pocs <npocs@redhat.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
---
include/libssh/session.h | 7 ++++
src/options.c | 91 ++++++++++++++++++++++++----------------
src/session.c | 2 +
3 files changed, 63 insertions(+), 37 deletions(-)
diff --git a/include/libssh/session.h b/include/libssh/session.h
index e22b0d67..cf219c2a 100644
--- a/include/libssh/session.h
+++ b/include/libssh/session.h
@@ -93,6 +93,12 @@ enum ssh_pending_call_e {
#define SSH_OPT_FLAG_KBDINT_AUTH 0x4
#define SSH_OPT_FLAG_GSSAPI_AUTH 0x8
+/* Escape expansion of different variables */
+#define SSH_OPT_EXP_FLAG_KNOWNHOSTS 0x1
+#define SSH_OPT_EXP_FLAG_GLOBAL_KNOWNHOSTS 0x2
+#define SSH_OPT_EXP_FLAG_PROXYCOMMAND 0x4
+#define SSH_OPT_EXP_FLAG_IDENTITY 0x8
+
/* extensions flags */
/* negotiation enabled */
#define SSH_EXT_NEGOTIATION 0x01
@@ -232,6 +238,7 @@ struct ssh_session_struct {
char *gss_client_identity;
int gss_delegate_creds;
int flags;
+ int exp_flags;
int nodelay;
bool config_processed;
uint8_t options_seen[SOC_MAX];
diff --git a/src/options.c b/src/options.c
index bb085384..c566244b 100644
--- a/src/options.c
+++ b/src/options.c
@@ -730,6 +730,7 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
ssh_set_error_oom(session);
return -1;
}
+ session->opts.exp_flags &= ~SSH_OPT_EXP_FLAG_KNOWNHOSTS;
}
break;
case SSH_OPTIONS_GLOBAL_KNOWNHOSTS:
@@ -751,6 +752,7 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
ssh_set_error_oom(session);
return -1;
}
+ session->opts.exp_flags &= ~SSH_OPT_EXP_FLAG_GLOBAL_KNOWNHOSTS;
}
break;
case SSH_OPTIONS_TIMEOUT:
@@ -1014,6 +1016,7 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
return -1;
}
session->opts.ProxyCommand = q;
+ session->opts.exp_flags &= ~SSH_OPT_EXP_FLAG_PROXYCOMMAND;
}
}
break;
@@ -1572,53 +1575,67 @@ int ssh_options_apply(ssh_session session)
}
}
- if (session->opts.knownhosts == NULL) {
- tmp = ssh_path_expand_escape(session, "%d/known_hosts");
- } else {
- tmp = ssh_path_expand_escape(session, session->opts.knownhosts);
- }
- if (tmp == NULL) {
- return -1;
+ if ((session->opts.exp_flags & SSH_OPT_EXP_FLAG_KNOWNHOSTS) == 0) {
+ if (session->opts.knownhosts == NULL) {
+ tmp = ssh_path_expand_escape(session, "%d/known_hosts");
+ } else {
+ tmp = ssh_path_expand_escape(session, session->opts.knownhosts);
+ }
+ if (tmp == NULL) {
+ return -1;
+ }
+ free(session->opts.knownhosts);
+ session->opts.knownhosts = tmp;
+ session->opts.exp_flags |= SSH_OPT_EXP_FLAG_KNOWNHOSTS;
}
- free(session->opts.knownhosts);
- session->opts.knownhosts = tmp;
- if (session->opts.global_knownhosts == NULL) {
- tmp = strdup("/etc/ssh/ssh_known_hosts");
- } else {
- tmp = ssh_path_expand_escape(session, session->opts.global_knownhosts);
- }
- if (tmp == NULL) {
- return -1;
+ if ((session->opts.exp_flags & SSH_OPT_EXP_FLAG_GLOBAL_KNOWNHOSTS) == 0) {
+ if (session->opts.global_knownhosts == NULL) {
+ tmp = strdup("/etc/ssh/ssh_known_hosts");
+ } else {
+ tmp = ssh_path_expand_escape(session,
+ session->opts.global_knownhosts);
+ }
+ if (tmp == NULL) {
+ return -1;
+ }
+ free(session->opts.global_knownhosts);
+ session->opts.global_knownhosts = tmp;
+ session->opts.exp_flags |= SSH_OPT_EXP_FLAG_GLOBAL_KNOWNHOSTS;
}
- free(session->opts.global_knownhosts);
- session->opts.global_knownhosts = tmp;
- if (session->opts.ProxyCommand != NULL) {
- char *p = NULL;
- size_t plen = strlen(session->opts.ProxyCommand) +
- 5 /* strlen("exec ") */;
- if (strncmp(session->opts.ProxyCommand, "exec ", 5) != 0) {
- p = malloc(plen + 1 /* \0 */);
- if (p == NULL) {
- return -1;
- }
+ if ((session->opts.exp_flags & SSH_OPT_EXP_FLAG_PROXYCOMMAND) == 0) {
+ if (session->opts.ProxyCommand != NULL) {
+ char *p = NULL;
+ size_t plen = strlen(session->opts.ProxyCommand) +
+ 5 /* strlen("exec ") */;
+
+ if (strncmp(session->opts.ProxyCommand, "exec ", 5) != 0) {
+ p = malloc(plen + 1 /* \0 */);
+ if (p == NULL) {
+ return -1;
+ }
- rc = snprintf(p, plen + 1, "exec %s", session->opts.ProxyCommand);
- if ((size_t)rc != plen) {
+ rc = snprintf(p, plen + 1, "exec %s", session->opts.ProxyCommand);
+ if ((size_t)rc != plen) {
+ free(p);
+ return -1;
+ }
+ tmp = ssh_path_expand_escape(session, p);
free(p);
- return -1;
+ } else {
+ tmp = ssh_path_expand_escape(session,
+ session->opts.ProxyCommand);
}
- }
- tmp = ssh_path_expand_escape(session, p);
- free(p);
- if (tmp == NULL) {
- return -1;
+ if (tmp == NULL) {
+ return -1;
+ }
+ free(session->opts.ProxyCommand);
+ session->opts.ProxyCommand = tmp;
+ session->opts.exp_flags |= SSH_OPT_EXP_FLAG_PROXYCOMMAND;
}
- free(session->opts.ProxyCommand);
- session->opts.ProxyCommand = tmp;
}
for (tmp = ssh_list_pop_head(char *, session->opts.identity_non_exp);
diff --git a/src/session.c b/src/session.c
index 34a492e4..06f6a26f 100644
--- a/src/session.c
+++ b/src/session.c
@@ -114,6 +114,8 @@ ssh_session ssh_new(void)
SSH_OPT_FLAG_KBDINT_AUTH |
SSH_OPT_FLAG_GSSAPI_AUTH;
+ session->opts.exp_flags = 0;
+
session->opts.identity = ssh_list_new();
if (session->opts.identity == NULL) {
goto err;
--
2.38.1
From 8849d0d89de7151a1e516ec373f570ba4678dde9 Mon Sep 17 00:00:00 2001
From: Norbert Pocs <npocs@redhat.com>
Date: Wed, 16 Nov 2022 17:17:14 +0100
Subject: [PATCH 4/5] torture_options.c: Add identity test for ssh_options_copy
Test if the ssh_options_apply is called on session before ssh_options_copy,
then `opts.identity` ssh_list will be copied
Signed-off-by: Norbert Pocs <npocs@redhat.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
---
tests/unittests/torture_options.c | 28 ++++++++++++++++++++++++++++
1 file changed, 28 insertions(+)
diff --git a/tests/unittests/torture_options.c b/tests/unittests/torture_options.c
index 3be2de8a..907cc8df 100644
--- a/tests/unittests/torture_options.c
+++ b/tests/unittests/torture_options.c
@@ -918,6 +918,34 @@ static void torture_options_copy(void **state)
sizeof(session->opts.options_seen));
ssh_free(new);
+
+ /* test if ssh_options_apply was called before ssh_options_copy
+ * the opts.identity list gets copied (percent expanded list) */
+ rv = ssh_options_apply(session);
+ assert_ssh_return_code(session, rv);
+
+ rv = ssh_options_copy(session, &new);
+ assert_ssh_return_code(session, rv);
+ assert_non_null(new);
+
+ it = ssh_list_get_iterator(session->opts.identity_non_exp);
+ assert_null(it);
+ it2 = ssh_list_get_iterator(new->opts.identity_non_exp);
+ assert_null(it2);
+
+ it = ssh_list_get_iterator(session->opts.identity);
+ assert_non_null(it);
+ it2 = ssh_list_get_iterator(new->opts.identity);
+ assert_non_null(it2);
+ while (it != NULL && it2 != NULL) {
+ assert_string_equal(it->data, it2->data);
+ it = it->next;
+ it2 = it2->next;
+ }
+ assert_null(it);
+ assert_null(it2);
+
+ ssh_free(new);
}
#define EXECUTABLE_NAME "test-exec"
--
2.38.1
From 88ef38bd1d95b07be4fa818462fb56fcca84cc5a Mon Sep 17 00:00:00 2001
From: Norbert Pocs <npocs@redhat.com>
Date: Wed, 16 Nov 2022 17:18:49 +0100
Subject: [PATCH 5/5] torture_options.c: Add test for ssh_options_apply
Test that ssh_options_apply can be called multiple times without expanding
escape characters more than once. If the options are updated after calling
ssh_options_apply keep the last options.
Signed-off-by: Norbert Pocs <npocs@redhat.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
---
tests/unittests/torture_options.c | 165 ++++++++++++++++++++++++++++++
1 file changed, 165 insertions(+)
diff --git a/tests/unittests/torture_options.c b/tests/unittests/torture_options.c
index 907cc8df..ea63b45e 100644
--- a/tests/unittests/torture_options.c
+++ b/tests/unittests/torture_options.c
@@ -1332,6 +1332,170 @@ static void torture_options_caret_sign(void **state)
free(awaited);
}
+static void torture_options_apply (void **state) {
+ ssh_session session = *state;
+ struct ssh_list *awaited_list = NULL;
+ struct ssh_iterator *it1 = NULL, *it2 = NULL;
+ char *id = NULL;
+ int rc;
+
+ rc = ssh_options_set(session,
+ SSH_OPTIONS_KNOWNHOSTS,
+ "%%d/.ssh/known_hosts");
+ assert_ssh_return_code(session, rc);
+
+ rc = ssh_options_set(session,
+ SSH_OPTIONS_GLOBAL_KNOWNHOSTS,
+ "/etc/%%u/libssh/known_hosts");
+ assert_ssh_return_code(session, rc);
+
+ rc = ssh_options_set(session,
+ SSH_OPTIONS_PROXYCOMMAND,
+ "exec echo \"Hello libssh %%d!\"");
+ assert_ssh_return_code(session, rc);
+
+ rc = ssh_options_set(session,
+ SSH_OPTIONS_ADD_IDENTITY,
+ "%%d/do_not_expand");
+ assert_ssh_return_code(session, rc);
+
+ rc = ssh_options_apply(session);
+ assert_ssh_return_code(session, rc);
+
+ /* check that the values got expanded */
+ assert_true(session->opts.exp_flags & SSH_OPT_EXP_FLAG_KNOWNHOSTS);
+ assert_true(session->opts.exp_flags & SSH_OPT_EXP_FLAG_GLOBAL_KNOWNHOSTS);
+ assert_true(session->opts.exp_flags & SSH_OPT_EXP_FLAG_PROXYCOMMAND);
+ assert_true(ssh_list_count(session->opts.identity_non_exp) == 0);
+ assert_true(ssh_list_count(session->opts.identity) > 0);
+
+ /* should not change anything calling it again */
+ rc = ssh_options_apply(session);
+ assert_ssh_return_code(session, rc);
+
+ /* check that the expansion was done only once */
+ assert_string_equal(session->opts.knownhosts, "%d/.ssh/known_hosts");
+ assert_string_equal(session->opts.global_knownhosts,
+ "/etc/%u/libssh/known_hosts");
+ /* no exec should be added if there already is one */
+ assert_string_equal(session->opts.ProxyCommand,
+ "exec echo \"Hello libssh %d!\"");
+ assert_string_equal(session->opts.identity->root->data,
+ "%d/do_not_expand");
+
+ /* apply should keep the freshest setting */
+ rc = ssh_options_set(session,
+ SSH_OPTIONS_KNOWNHOSTS,
+ "hello there");
+ assert_ssh_return_code(session, rc);
+
+ rc = ssh_options_set(session,
+ SSH_OPTIONS_GLOBAL_KNOWNHOSTS,
+ "lorem ipsum");
+ assert_ssh_return_code(session, rc);
+
+ rc = ssh_options_set(session,
+ SSH_OPTIONS_PROXYCOMMAND,
+ "mission_impossible");
+ assert_ssh_return_code(session, rc);
+
+ rc = ssh_options_set(session,
+ SSH_OPTIONS_ADD_IDENTITY,
+ "007");
+ assert_ssh_return_code(session, rc);
+
+ rc = ssh_options_set(session,
+ SSH_OPTIONS_ADD_IDENTITY,
+ "3");
+ assert_ssh_return_code(session, rc);
+
+ rc = ssh_options_set(session,
+ SSH_OPTIONS_ADD_IDENTITY,
+ "2");
+ assert_ssh_return_code(session, rc);
+
+ rc = ssh_options_set(session,
+ SSH_OPTIONS_ADD_IDENTITY,
+ "1");
+ assert_ssh_return_code(session, rc);
+
+ /* check that flags show need of escape expansion */
+ assert_false(session->opts.exp_flags & SSH_OPT_EXP_FLAG_KNOWNHOSTS);
+ assert_false(session->opts.exp_flags & SSH_OPT_EXP_FLAG_GLOBAL_KNOWNHOSTS);
+ assert_false(session->opts.exp_flags & SSH_OPT_EXP_FLAG_PROXYCOMMAND);
+ assert_false(ssh_list_count(session->opts.identity_non_exp) == 0);
+
+ rc = ssh_options_apply(session);
+ assert_ssh_return_code(session, rc);
+
+ /* check that the values got expanded */
+ assert_true(session->opts.exp_flags & SSH_OPT_EXP_FLAG_KNOWNHOSTS);
+ assert_true(session->opts.exp_flags & SSH_OPT_EXP_FLAG_GLOBAL_KNOWNHOSTS);
+ assert_true(session->opts.exp_flags & SSH_OPT_EXP_FLAG_PROXYCOMMAND);
+ assert_true(ssh_list_count(session->opts.identity_non_exp) == 0);
+
+ assert_string_equal(session->opts.knownhosts, "hello there");
+ assert_string_equal(session->opts.global_knownhosts, "lorem ipsum");
+ /* check that the "exec " was added at the beginning */
+ assert_string_equal(session->opts.ProxyCommand, "exec mission_impossible");
+ assert_string_equal(session->opts.identity->root->data, "1");
+
+ /* check the order of the identity files after double expansion */
+ awaited_list = ssh_list_new();
+ /* append the new data in order */
+ id = strdup("1");
+ rc = ssh_list_append(awaited_list, id);
+ assert_int_equal(rc, SSH_OK);
+ id = strdup("2");
+ rc = ssh_list_append(awaited_list, id);
+ assert_int_equal(rc, SSH_OK);
+ id = strdup("3");
+ rc = ssh_list_append(awaited_list, id);
+ assert_int_equal(rc, SSH_OK);
+ id = strdup("007");
+ rc = ssh_list_append(awaited_list, id);
+ assert_int_equal(rc, SSH_OK);
+ id = strdup("%d/do_not_expand");
+ rc = ssh_list_append(awaited_list, id);
+ assert_int_equal(rc, SSH_OK);
+ /* append the defaults; this list is copied from ssh_new@src/session.c */
+ id = ssh_path_expand_escape(session, "%d/id_ed25519");
+ rc = ssh_list_append(awaited_list, id);
+ assert_int_equal(rc, SSH_OK);
+#ifdef HAVE_ECC
+ id = ssh_path_expand_escape(session, "%d/id_ecdsa");
+ rc = ssh_list_append(awaited_list, id);
+ assert_int_equal(rc, SSH_OK);
+#endif
+ id = ssh_path_expand_escape(session, "%d/id_rsa");
+ rc = ssh_list_append(awaited_list, id);
+ assert_int_equal(rc, SSH_OK);
+#ifdef HAVE_DSA
+ id = ssh_path_expand_escape(session, "%d/id_dsa");
+ rc = ssh_list_append(awaited_list, id);
+ assert_int_equal(rc, SSH_OK);
+#endif
+
+ assert_int_equal(ssh_list_count(awaited_list),
+ ssh_list_count(session->opts.identity));
+
+ it1 = ssh_list_get_iterator(awaited_list);
+ assert_non_null(it1);
+ it2 = ssh_list_get_iterator(session->opts.identity);
+ assert_non_null(it2);
+ while (it1 != NULL && it2 != NULL) {
+ assert_string_equal(it1->data, it2->data);
+
+ free((void*)it1->data);
+ it1 = it1->next;
+ it2 = it2->next;
+ }
+ assert_null(it1);
+ assert_null(it2);
+
+ ssh_list_free(awaited_list);
+}
+
#ifdef WITH_SERVER
const char template[] = "temp_dir_XXXXXX";
@@ -2132,6 +2296,7 @@ int torture_run_tests(void) {
setup, teardown),
cmocka_unit_test_setup_teardown(torture_options_caret_sign,
setup, teardown),
+ cmocka_unit_test_setup_teardown(torture_options_apply, setup, teardown),
};
#ifdef WITH_SERVER
--
2.38.1

1653
plus_sign.patch Normal file

File diff suppressed because it is too large Load Diff