From cc7ee95acd6b8fcfe141da1858f3da8012e860e5 Mon Sep 17 00:00:00 2001
From: Martin Hecht <mrbaseman@gmx.de>
Date: Thu, 28 Nov 2019 15:14:08 +0100
Subject: [PATCH] allow logging in with a certificate only (#493)

...and with password set to an empty string, which denotes that no password shall be asked or sent
allow username to be empty for smartcard login
update man page and readme
---
 README.md             |  3 +--
 doc/openfortivpn.1.in |  8 ++++----
 src/http.c            | 15 ++++++++++-----
 src/main.c            | 19 +++++++------------
 4 files changed, 22 insertions(+), 23 deletions(-)

diff --git a/README.md b/README.md
index 9cb1e48..375d6f4 100644
--- a/README.md
+++ b/README.md
@@ -55,8 +55,7 @@ Check [#464](https://github.com/adrienverge/openfortivpn/issues/464) for a discu
 of known issues in this area.
 
 To make use of your smartcard put at least `pkcs11:` to the user-cert config or commandline
-option. It takes the full or a partial PKCS#11 token URI. Also username and password currently
-may not be empty, but don't get used. So you should fill in dummy values.
+option. It takes the full or a partial PKCS#11 token URI.
 
 ```
 user-cert = pkcs11:
diff --git a/doc/openfortivpn.1.in b/doc/openfortivpn.1.in
index bd73a8b..a4ed34b 100644
--- a/doc/openfortivpn.1.in
+++ b/doc/openfortivpn.1.in
@@ -1,4 +1,4 @@
-.TH OPENFORTIVPN 1 "October 24, 2019" ""
+.TH OPENFORTIVPN 1 "November 27, 2019" ""
 
 .SH NAME
 openfortivpn \- Client for PPP+SSL VPN tunnel services
@@ -110,8 +110,8 @@ Use specified PEM-encoded certificate if the server requires authentication
 with a certificate.
 .TP
 \fB\-\-user-cert=\fIpkcs11:\fR
-Use at least the string pkcs11: for using a smartcard. Takes also
-full or partially PKCS11-URI (p11tool --list-token-urls)
+Use at least the string pkcs11: for using a smartcard. It takes the full
+or a partial PKCS11-URI (p11tool --list-token-urls)
 
   --user-cert = pkcs11:
 
@@ -119,7 +119,7 @@ full or partially PKCS11-URI (p11tool --list-token-urls)
 
   --user-cert = pkcs11:model=PKCS%2315%20emulated;manufacturer=piv_II;serial=012345678;token=someuser
 
-\fBRequires OpenSSL PKCS engine!
+\fBThis feature requires OpenSSL PKCS engine!
 .TP
 \fB\-\-user\-key=\fI<file>\fR
 Use specified PEM-encoded key if the server requires authentication with
diff --git a/src/http.c b/src/http.c
index 8fe7a35..c29d0d8 100644
--- a/src/http.c
+++ b/src/http.c
@@ -566,7 +566,6 @@ int auth_log_in(struct tunnel *tunnel)
 	uint32_t response_size;
 
 	url_encode(username, tunnel->config->username);
-	url_encode(password, tunnel->config->password);
 	url_encode(realm, tunnel->config->realm);
 
 	tunnel->cookie[0] = '\0';
@@ -576,10 +575,16 @@ int auth_log_in(struct tunnel *tunnel)
 		ret = http_request(tunnel, "GET", "/remote/login",
 		                   data, &res, &response_size);
 	} else {
-		snprintf(data, sizeof(data), "username=%s&credential=%s&realm=%s&ajax=1"
-		         "&redir=%%2Fremote%%2Findex&just_logged_in=1",
-		         username, password, realm);
-
+		if (tunnel->config->password == '\0') {
+			snprintf(data, sizeof(data), "username=%s&realm=%s&ajax=1"
+			         "&redir=%%2Fremote%%2Findex&just_logged_in=1",
+			         username, realm);
+		} else {
+			url_encode(password, tunnel->config->password);
+			snprintf(data, sizeof(data), "username=%s&credential=%s&realm=%s&ajax=1"
+			         "&redir=%%2Fremote%%2Findex&just_logged_in=1",
+			         username, password, realm);
+		}
 		ret = http_request(tunnel, "POST", "/remote/logincheck",
 		                   data, &res, &response_size);
 	}
diff --git a/src/main.c b/src/main.c
index b283c8f..a99e65c 100644
--- a/src/main.c
+++ b/src/main.c
@@ -512,30 +512,25 @@ int main(int argc, char **argv)
 		goto user_error;
 	}
 	// Check username
-	if (cfg.username[0] == '\0') {
+	if (cfg.username[0] == '\0' && cfg.use_engine != 1) {
 		log_error("Specify an username.\n");
 		goto user_error;
 	}
-	// If no password given, interactively ask user
-	if (cfg.password == NULL || cfg.password[0] == '\0') {
-		free(cfg.password);
+	// If username but no password given, interactively ask user
+	if (cfg.password == NULL && cfg.username[0] != '\0' ) {
 		char *tmp_password = malloc(PWD_BUFSIZ); // allocate large buffer
 		read_password(cfg.pinentry, "password",
 		              "VPN account password: ", tmp_password, PWD_BUFSIZ);
 		cfg.password = strdup(tmp_password); // copy string of correct size
 		free(tmp_password);
 	}
-	// Check password
-	if (cfg.password[0] == '\0') {
-		log_error("Specify a password.\n");
-		goto user_error;
-	}
-
 	log_debug("Config host = \"%s\"\n", cfg.gateway_host);
 	log_debug("Config realm = \"%s\"\n", cfg.realm);
 	log_debug("Config port = \"%d\"\n", cfg.gateway_port);
-	log_debug("Config username = \"%s\"\n", cfg.username);
-	log_debug_all("Config password = \"%s\"\n", cfg.password);
+	if (cfg.username[0] != '\0')
+		log_debug("Config username = \"%s\"\n", cfg.username);
+	if (cfg.password != NULL)
+		log_debug_all("Config password = \"%s\"\n", cfg.password);
 	if (cfg.otp[0] != '\0')
 		log_debug("One-time password = \"%s\"\n", cfg.otp);
 
-- 
GitLab