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