diff --git a/doc/openfortivpn.1.in b/doc/openfortivpn.1.in index a2346440..2f08ac96 100644 --- a/doc/openfortivpn.1.in +++ b/doc/openfortivpn.1.in @@ -11,6 +11,7 @@ openfortivpn \- Client for PPP+SSL VPN tunnel services [\fB\-\-otp=\fI\fR] [\fB\-\-otp\-prompt=\fI\fR] [\fB\-\-otp\-delay=\fI\fR] +[\fB\-\-no\-ftm\-push\fR] [\fB\-\-realm=\fI\fR] [\fB\-\-set\-routes=\fR] [\fB\-\-no\-routes\fR] @@ -79,6 +80,12 @@ Set the amount of time to wait before sending the One-Time-Password. The delay time must be specified in seconds, where 0 means no wait (this is the default). .TP +\fB\-\-no\-ftm\-push\fR +Do not use FTM push if the server provides the option. +The server may be configured to allow two factor authentication through a +push notification to the mobile application. If this option is provided, +authentication based on OTP will be used instead. +.TP \fB\-\-realm=\fI\fR Connect to the specified authentication realm. Defaults to empty, which is usually what you want. @@ -286,6 +293,10 @@ password = bar .br # otp\-prompt = Please .br +# This would disable FTM push notification support, and use OTP instead +.br +# no\-ftm\-push = 1 +.br # pinentry = pinentry program .br user\-cert = @SYSCONFDIR@/openfortivpn/user\-cert.pem diff --git a/src/config.c b/src/config.c index e1fc64ab..f177eb9c 100644 --- a/src/config.c +++ b/src/config.c @@ -48,6 +48,7 @@ const struct vpn_config invalid_cfg = { .otp = {'\0'}, .otp_prompt = NULL, .otp_delay = -1, + .no_ftm_push = -1, .pinentry = NULL, .realm = {'\0'}, .set_routes = -1, @@ -277,6 +278,15 @@ int load_config(struct vpn_config *cfg, const char *filename) continue; } cfg->otp_delay = otp_delay; + } else if (strcmp(key, "no-ftm-push") == 0) { + int no_ftm_push = strtob(val); + + if (no_ftm_push < 0) { + log_warn("Bad no-ftm-push in config file: \"%s\".\n", + val); + continue; + } + cfg->no_ftm_push = no_ftm_push; } else if (strcmp(key, "pinentry") == 0) { free(cfg->pinentry); cfg->pinentry = strdup(val); @@ -500,6 +510,8 @@ void merge_config(struct vpn_config *dst, struct vpn_config *src) strcpy(dst->otp, src->otp); if (src->otp_delay != invalid_cfg.otp_delay) dst->otp_delay = src->otp_delay; + if (src->no_ftm_push != invalid_cfg.no_ftm_push) + dst->no_ftm_push = src->no_ftm_push; if (src->pinentry) { free(dst->pinentry); dst->pinentry = src->pinentry; diff --git a/src/config.h b/src/config.h index 5587a377..c27b8dfd 100644 --- a/src/config.h +++ b/src/config.h @@ -85,6 +85,7 @@ struct vpn_config { char otp[FIELD_SIZE + 1]; char *otp_prompt; unsigned int otp_delay; + int no_ftm_push; char *pinentry; char realm[FIELD_SIZE + 1]; diff --git a/src/http.c b/src/http.c index 52548751..e06f2ce7 100644 --- a/src/http.c +++ b/src/http.c @@ -632,7 +632,7 @@ int auth_log_in(struct tunnel *tunnel) char portal[64] = { '\0' }; char magic[32] = {'\0' }; char peer[32] = { '\0' }; - char data[1024], token[128], tokenresponse[256]; + char data[1152], token[128], tokenresponse[256], tokenparams[320]; char action_url[1024] = { '\0' }; char *res = NULL; uint32_t response_size; @@ -711,21 +711,38 @@ int auth_log_in(struct tunnel *tunnel) get_value_from_response(res, "magic=", magic, 32); get_value_from_response(res, "peer=", peer, 32); - if (cfg->otp[0] == '\0') { - read_password(cfg->pinentry, "otp", - "Two-factor authentication token: ", - cfg->otp, FIELD_SIZE); + if (cfg->otp[0] == '\0' && + strncmp(token, "ftm_push", 8) == 0 && + cfg->no_ftm_push == 0) { + /* + * The server supports FTM push if `tokeninfo` is `ftm_push`, + * but only try this if the OTP is not provided by the config + * file or command line. + */ + snprintf(tokenparams, sizeof(tokenparams), "ftmpush=1"); + } else { if (cfg->otp[0] == '\0') { - log_error("No token specified\n"); - return 0; + // Prompt for OTP token + read_password(cfg->pinentry, "otp", + "Two-factor authentication token: ", + cfg->otp, FIELD_SIZE); + + if (cfg->otp[0] == '\0') { + log_error("No token specified\n"); + return 0; + } } + + url_encode(tokenresponse, cfg->otp); + snprintf(tokenparams, sizeof(tokenparams), + "code=%s&code2=&magic=%s", + tokenresponse, magic); } - url_encode(tokenresponse, cfg->otp); snprintf(data, sizeof(data), - "username=%s&realm=%s&reqid=%s&polid=%s&grp=%s&code=%s&code2=&portal=%s&peer=%s&magic=%s", - username, realm, reqid, polid, group, tokenresponse, - portal, peer, magic); + "username=%s&realm=%s&reqid=%s&polid=%s&grp=%s&portal=%s&peer=%s&%s", + username, realm, reqid, polid, group, portal, peer, + tokenparams); delay_otp(tunnel); ret = http_request(tunnel, "POST", "/remote/logincheck", diff --git a/src/main.c b/src/main.c index 0643f155..591ba813 100644 --- a/src/main.c +++ b/src/main.c @@ -117,6 +117,7 @@ PPPD_USAGE \ " -o , --otp= One-Time-Password.\n" \ " --otp-prompt= Search for the OTP prompt starting with this string\n" \ " --otp-delay= Wait seconds before sending the OTP.\n" \ +" --no-ftm-push Do not use FTM push if the server provides the option.\n" \ " --pinentry= Use the program to supply a secret instead of asking for it\n" \ " --realm= Use specified authentication realm.\n" \ " --set-routes=[01] Set if openfortivpn should configure routes\n" \ @@ -197,6 +198,7 @@ int main(int argc, char **argv) .otp = {'\0'}, .otp_prompt = NULL, .otp_delay = 0, + .no_ftm_push = 0, .pinentry = NULL, .realm = {'\0'}, .set_routes = 1, @@ -245,6 +247,7 @@ int main(int argc, char **argv) {"otp", required_argument, NULL, 'o'}, {"otp-prompt", required_argument, NULL, 0}, {"otp-delay", required_argument, NULL, 0}, + {"no-ftm-push", no_argument, &cli_cfg.no_ftm_push, 1}, {"set-routes", required_argument, NULL, 0}, {"no-routes", no_argument, &cli_cfg.set_routes, 0}, {"half-internet-routes", required_argument, NULL, 0},