From b4b5edc2866289bcde392f307ae9343dfb575a98 Mon Sep 17 00:00:00 2001
From: Lukas Braun <koomi@moshbit.net>
Date: Sat, 7 Oct 2017 21:42:34 +0200
Subject: [PATCH] pam_goatherd: Handle IPv6 bracket notation

---
 pam_goatherd.c       | 53 +++++++++++++++++++++++++++++++++++++-------
 test_pam_goatherd.sh |  4 ++--
 2 files changed, 47 insertions(+), 10 deletions(-)

diff --git a/pam_goatherd.c b/pam_goatherd.c
index 825fbb1..861c826 100644
--- a/pam_goatherd.c
+++ b/pam_goatherd.c
@@ -141,7 +141,7 @@ static int check_hotp(struct cfg cfg, size_t n_server, const char *user, const c
 
 	// resolve name and connect
     dbgp("connecting...");
-    struct addrinfo hint = { .ai_socktype = SOCK_STREAM };
+    struct addrinfo hint = { .ai_socktype = SOCK_STREAM, .ai_family = AF_UNSPEC };
     struct addrinfo *addris_start;
     if ((err = getaddrinfo(cfg.servers[n_server], cfg.ports[n_server], &hint, &addris_start)) != 0)
     {
@@ -272,14 +272,51 @@ pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv)
                 // allocate on the stack and avoid free() headaches (needs
                 // _GNU_SOURCE).
                 char *serverport = strdupa(&argv[i][strlen(arg_server)]);
-                char *saveptr;
 
-                // TODO: handle IPv6 addrs
-                cfg.servers[n_servers] = strtok_r(serverport, ":", &saveptr);
-                cfg.ports[n_servers] = strtok_r(NULL, ":", &saveptr);
-                if (!cfg.ports[n_servers]) {
-                    dbgp2("Fatal: need port for", serverport);
-                    return PAM_AUTHINFO_UNAVAIL;
+                if (serverport[0] == '\0')
+                {
+                    continue;
+                }
+
+                // Gotta handle IPv6 addresses differently because they contain colons
+                if (serverport[0] == '[')
+                {
+                    char *closing_bracket = strchr(serverport+1, ']');
+                    if (!closing_bracket)
+                    {
+                        dbgp2("Fatal: expecting closing bracket in server address", serverport);
+                        return PAM_AUTHINFO_UNAVAIL;
+                    }
+                    if (closing_bracket[1] != ':')
+                    {
+                        dbgp2("Fatal: expecting :port after closing bracket in server address", serverport);
+                        return PAM_AUTHINFO_UNAVAIL;
+                    }
+                    if (closing_bracket[2] == '\0')
+                    {
+                        dbgp2("Fatal: expecting port after colon in server address", serverport);
+                        return PAM_AUTHINFO_UNAVAIL;
+                    }
+
+                    closing_bracket[0] = '\0';
+                    cfg.servers[n_servers] = serverport+1;
+                    cfg.ports[n_servers] = closing_bracket+2;
+                } else {
+                    char *colon = strchr(serverport, ':');
+                    if (!colon)
+                    {
+                        dbgp2("Fatal: need port for", serverport);
+                        return PAM_AUTHINFO_UNAVAIL;
+                    }
+                    if (colon[1] == '\0')
+                    {
+                        dbgp2("Fatal: expecting port after colon in server address", serverport);
+                        return PAM_AUTHINFO_UNAVAIL;
+                    }
+
+                    colon[0] = '\0';
+                    cfg.servers[n_servers] = serverport;
+                    cfg.ports[n_servers] = colon+1;
                 }
 
                 n_servers++;
diff --git a/test_pam_goatherd.sh b/test_pam_goatherd.sh
index 6611a8b..e20b0cc 100644
--- a/test_pam_goatherd.sh
+++ b/test_pam_goatherd.sh
@@ -51,7 +51,7 @@ else
     echo "(failure expected)"
 fi
 
-ghdb 1 serve -addr 127.0.0.1:9989 -tls-key test.key -tls-cert test.crt &
+ghdb 1 serve -addr [::1]:9989 -tls-key test.key -tls-cert test.crt &
 
 echo blub | if pw pamtester single_server someuser authenticate; then
     die "FAIL: Auth succeeded but user is not valid"
@@ -77,7 +77,7 @@ fi
 echo
 echo "== Multi server tests =="
 
-echo auth requisite $PWD/pam_goatherd.so certs=test.crt server=localhost:9989 server=localhost:9988 server=localhost:9987 >$servicedir/three_servers
+echo auth requisite $PWD/pam_goatherd.so certs=test.crt server=[::1]:9989 server=127.0.0.1:9988 server=localhost:9987 >$servicedir/three_servers
 
 oathtool -d 8 -c 2 $(echo -n $secret | xxd -p) | \
     if pw pamtester three_servers someuser authenticate; then
-- 
GitLab