Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support SAML authentication #1241

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

bin_PROGRAMS = openfortivpn
openfortivpn_SOURCES = src/config.c src/config.h src/hdlc.c src/hdlc.h \
src/http.c src/http.h src/io.c src/io.h src/ipv4.c \
src/http.c src/http.h src/io.c src/io.h \
src/http_server.c src/ipv4.c \
src/ipv4.h src/log.c src/log.h src/tunnel.c \
src/tunnel.h src/main.c src/ssl.h src/xml.c \
src/xml.h src/userinput.c src/userinput.h
Expand Down
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ Examples
openfortivpn vpn-gateway:8443 --username= --password= --user-cert=cert.pem --user-key=key.pem
```

* Connect using SAML login:
```shell
openfortivpn vpn-gateway:8443 --saml-login
```

* Don't set IP routes and don't add VPN nameservers to `/etc/resolv.conf`:
```shell
openfortivpn vpn-gateway:8443 -u foo --no-routes --no-dns --pppd-no-peerdns
Expand Down Expand Up @@ -230,6 +235,12 @@ to authenticate and retrieve a session cookie. This cookie can be fed
to openfortivpn using option `--cookie-on-stdin`. Obviously, such a
solution requires a graphic session.

When started using `--saml-login` the program creates a web server that
accepts SAML login requests. To login using SAML you just have to open
`<your-vpn-domain>/remote/saml/start?redirect=1` and follow the login steps.
At the end of the login process the page will be redirected to
`http://127.0.0.1:8020/?id=<session-id>`

Contributing
------------

Expand Down
9 changes: 9 additions & 0 deletions doc/openfortivpn.1.in
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ openfortivpn \- Client for PPP+TLS VPN tunnel services
[\fB\-p\fR \fI<pass>\fR]
[\fB\-\-cookie=\fI<cookie>\fR]
[\fB\-\-cookie\-on\-stdin\fR]
[\fB\-\-saml\-login[=\fI<port>\fR]]
[\fB\-\-pinentry=\fI<name>\fR]
[\fB\-\-otp=\fI<otp>\fR]
[\fB\-\-otp\-prompt=\fI<prompt>\fR]
Expand Down Expand Up @@ -81,6 +82,14 @@ A valid cookie (SVPNCOOKIE) to use in place of username and password.
\fB\-\-cookie\-on\-stdin\fR
Read the cookie (SVPNCOOKIE) from standard input.
.TP
\fB\-\-saml\-login[=\fI<port>\fR]
Create a temporary web server to receive a local SAML redirect operation.
To login using SAML you just have to open
`<your-vpn-domain>/remote/saml/start?redirect=1' and follow the login steps.
At the end of the login process, the page will be redirected to
`http://127.0.0.1:8020/?id=<session-id>'. The actual URL to use for the login,
including the optional ream, is printed to the terminal when the web server it started.
.TP
\fB\-\-pinentry=\fI<name>\fR
The pinentry program to use. Allows supplying the password in a secure manner.
For example: pinentry-gnome3 on Linux, or pinentry-mac on macOS.
Expand Down
2 changes: 2 additions & 0 deletions etc/openfortivpn/config.template
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ host = vpn.example.org
port = 443
username = vpnuser
password = VPNpassw0rd

# saml-login = 8020
13 changes: 13 additions & 0 deletions src/config.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ const struct vpn_config invalid_cfg = {
.password = {'\0'},
.password_set = 0,
.cookie = NULL,
.saml_port = 0,
.saml_session_id = {'\0'},
.otp = {'\0'},
.otp_prompt = NULL,
.otp_delay = -1,
Expand Down Expand Up @@ -417,6 +419,14 @@ int load_config(struct vpn_config *cfg, const char *filename)
cfg->user_cert = strdup(val);
if (strncmp(cfg->user_cert, "pkcs11:", 7) == 0)
cfg->use_engine = 1;
} else if (strcmp(key, "saml-login") == 0) {
long port = strtol(val, NULL, 0);

if (port < 1 || port > 65535) {
log_error("Bad SAML listen port: \"%s\".\n", val);
goto err_free;
}
cfg->saml_port = (uint16_t)port;
} else if (strcmp(key, "user-key") == 0) {
free(cfg->user_key);
cfg->user_key = strdup(val);
Expand Down Expand Up @@ -534,6 +544,9 @@ void merge_config(struct vpn_config *dst, struct vpn_config *src)
free(dst->cookie);
dst->cookie = src->cookie;
}
if(src->saml_port != 0) {
dst->saml_port = src->saml_port;
}
if (src->pinentry) {
free(dst->pinentry);
dst->pinentry = src->pinentry;
Expand Down
3 changes: 3 additions & 0 deletions src/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ struct x509_digest {
* We believe we are on the safe side using this value.
*/
#define MAX_DOMAIN_LENGTH 256
#define MAX_SAML_SESSION_ID_LENGTH 1024

struct vpn_config {
char gateway_host[GATEWAY_HOST_SIZE + 1];
Expand All @@ -91,6 +92,8 @@ struct vpn_config {
int password_set;
char otp[OTP_SIZE + 1];
char *cookie;
int saml_port;
char saml_session_id[MAX_SAML_SESSION_ID_LENGTH];
char *otp_prompt;
unsigned int otp_delay;
int no_ftm_push;
Expand Down
4 changes: 2 additions & 2 deletions src/hdlc.c
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ ssize_t hdlc_find_frame(const uint8_t *buffer, size_t bufsize, off_t *start)
int s = -1, e = -1;

// Look for frame start
for (int i = *start; i < bufsize; i++) {
for (size_t i = *start; i < bufsize; i++) {
if (buffer[i] == 0x7e) { // Flag Sequence
s = i + 1;
break;
Expand All @@ -213,7 +213,7 @@ ssize_t hdlc_find_frame(const uint8_t *buffer, size_t bufsize, off_t *start)
s++;

// Look for frame end
for (int i = s; i < bufsize; i++) {
for (size_t i = s; i < bufsize; i++) {
if (buffer[i] == 0x7e) { // Flag Sequence
e = i;
break;
Expand Down
12 changes: 10 additions & 2 deletions src/http.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
* @param[out] dest the buffer to write the URL-encoded string
* @param[in] str the input string to be escaped
*/
static void url_encode(char *dest, const char *str)
void url_encode(char *dest, const char *str)
{
while (*str != '\0') {
if (isalnum(*str) || *str == '-' || *str == '_' ||
Expand Down Expand Up @@ -667,7 +667,15 @@ int auth_log_in(struct tunnel *tunnel)

tunnel->cookie[0] = '\0';

if (username[0] == '\0' && tunnel->config->password[0] == '\0') {
if (strlen(tunnel->config->saml_session_id) > 0) {
// SAML login
static const char *uri_pattern = "/remote/saml/auth_id?id=%s";
int required_size = snprintf(NULL, 0, uri_pattern, tunnel->config->saml_session_id) + 1;
char *uri = alloca(required_size);
snprintf(uri, required_size, uri_pattern, tunnel->config->saml_session_id);
log_debug("Using SAML authentication URL %s\n", uri);
ret = http_request(tunnel, "GET", uri, "", &res, &response_size);
} else if (username[0] == '\0' && tunnel->config->password[0] == '\0') {
ret = http_request(tunnel, "GET", "/remote/login",
data, &res, &response_size);
} else {
Expand Down
10 changes: 10 additions & 0 deletions src/http.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,16 @@
#define ERR_HTTP_PERMISSION -6
#define ERR_HTTP_NO_COOKIE -7

/*
* URL-encodes a string for HTTP requests.
*
* The dest buffer size MUST be at least strlen(str) * 3 + 1.
*
* @param[out] dest the buffer to write the URL-encoded string
* @param[in] str the input string to be escaped
*/
void url_encode(char *dest, const char *str);

static inline const char *err_http_str(int code)
{
if (code > 0)
Expand Down
Loading