diff --git a/programs/pluto/ikev1_quick.c b/programs/pluto/ikev1_quick.c index 7bb4a278d54..e043c027347 100644 --- a/programs/pluto/ikev1_quick.c +++ b/programs/pluto/ikev1_quick.c @@ -83,6 +83,8 @@ #include "alg_info.h" #include "ip_address.h" +#include "pam_conv.h" /* needs struct xauth */ + #include /* accept_PFS_KE @@ -1987,5 +1989,18 @@ stf_status quick_inI2(struct state *st, struct msg_digest *md) } } + /* + * Tell the xauth thread it's finally time to start the session for + * e.g., billing. We need to find the phase1 state, though, since + * that's where the xauth is. + */ + struct state *p1st = state_with_serialno(st->st_clonedfrom); + if (!p1st || !p1st->st_xauth) { + delete_ipsec_sa(st); + return STF_FAIL; + + } + atomic_flag_clear(&p1st->st_xauth->vpn_still_starting); + return STF_OK; } diff --git a/programs/pluto/pam_conv.c b/programs/pluto/pam_conv.c index 1108e2c2a4c..249660bbfd0 100644 --- a/programs/pluto/pam_conv.c +++ b/programs/pluto/pam_conv.c @@ -30,6 +30,7 @@ #include #include #include /* needed for pam_handle_t */ +#include #include "defs.h" #include "lswlog.h" @@ -41,9 +42,10 @@ #include "log.h" #include "state.h" +extern char **environ; /* because apparently not _GNU_SOURCE */ static char *pam_state_enum[] = { - "PAM_AUTH","PAM_SESSION_START", "PAM_SESSION_END", "PAM_TERM", "PAM_STATE_UNKNOWN", "PAM_DO_NOTHING" + "PAM_AUTH", "PAM_CALLBACK", "PAM_SESSION_START", "PAM_SESSION_END", "PAM_TERM", "PAM_STATE_UNKNOWN", "PAM_DO_NOTHING" }; static char *pam_result_state_enum[] = { "PAM_AUTH_SUCCESS", "PAM_AUTH_FAIL", "PAM_SESSION_START_SUCCESS", "PAM_SESSION_START_FAIL", "PAM_SESSION_END_SUCCESS", "PAM_SESSION_END_FAIL", "PAM_TERM_SUCCESS", "PAM_TERM_FAIL", "PAM_RESULT_UNKNOWN" @@ -223,7 +225,7 @@ void *pam_thread(void *parg) if (retval == PAM_SUCCESS) { /* do promotion to session start */ ptr_xauth->ptarg.pam_state = PAM_AUTH_SUCCESS; - ptr_xauth->ptarg.pam_do_state = PAM_SESSION_START; + ptr_xauth->ptarg.pam_do_state = PAM_CALLBACK; break; // break out of pam_acct_mgmt loop @@ -255,41 +257,74 @@ void *pam_thread(void *parg) } } + } else if(ptr_xauth->ptarg.pam_do_state == PAM_CALLBACK) { + log_pam_step((struct pam_thread_arg *)&ptr_xauth->ptarg, "making xauth callback"); - } else if(ptr_xauth->ptarg.pam_do_state == PAM_SESSION_START) { + struct state *st = state_with_serialno(ptr_xauth->serialno); + if(st != NULL) { + so_serial_t old_state = push_cur_state(st); - for (int i = 0; i < 5; i++) { + libreswan_log("XAUTH: #%lu: completed for user '%s' (waiting for VPN up for session_start)", + ptr_xauth->ptarg.st_serialno, ptr_xauth->ptarg.name); + + ptr_xauth->callback(st, ptr_xauth->ptarg.name, TRUE); + pop_cur_state(old_state); + } + ptr_xauth->ptarg.pam_do_state = PAM_SESSION_START; + } else if(ptr_xauth->ptarg.pam_do_state == PAM_SESSION_START) { + if (atomic_flag_test_and_set(&ptr_xauth->vpn_still_starting)) { + log_pam_step(&ptr_xauth->ptarg, "waiting for VPN up"); + } else { what = "pam_open_session"; retval = pam_open_session(pamh, PAM_SILENT); log_pam_step((struct pam_thread_arg *)&ptr_xauth->ptarg, what); - if (retval == PAM_SUCCESS) { - - bool success = TRUE; - struct state *st = state_with_serialno(ptr_xauth->serialno); - //passert(st != NULL); - if(st != NULL) { - so_serial_t old_state = push_cur_state(st); - libreswan_log("XAUTH: #%lu: completed for user '%s' with status %s ::: pam_open_session", - ptr_xauth->ptarg.st_serialno, ptr_xauth->ptarg.name, - success ? "SUCCESSS" : "FAILURE"); - - ptr_xauth->callback(st, ptr_xauth->ptarg.name, success); - pop_cur_state(old_state); - } + struct state *st = state_with_serialno(ptr_xauth->serialno); + if (!st) { + /* should never happen, just log if it does */ + libreswan_log("XAUTH: #%lu: INTERNAL ERROR Could not find SA state object for user '%s' ::: pam_open_session", + ptr_xauth->ptarg.st_serialno, ptr_xauth->ptarg.name); + } + libreswan_log("XAUTH: #%lu: completed for user '%s' with status %s ::: pam_open_session", + ptr_xauth->ptarg.st_serialno, ptr_xauth->ptarg.name, + (retval == PAM_SUCCESS ? "SUCCESS" : "FAILURE")); + if (retval == PAM_SUCCESS) { ptr_xauth->ptarg.pam_state = PAM_SESSION_START_SUCCESS; ptr_xauth->ptarg.pam_do_state = PAM_DO_NOTHING; - - break; } else { ptr_xauth->ptarg.pam_state = PAM_SESSION_START_FAIL; ptr_xauth->ptarg.pam_do_state = PAM_TERM; - } - } - /* Failed pam_open_session */ + /* + * Ideally, sending the message would be a library, but it's + * not... so only way to re-use that code is to just call ipsec + * whack. Hopefully posix_spawn avoids the pthread/fork issues. + */ + pid_t child; + int res; + char buf[32]; /* 2^64-1 needs 21 */ + res = snprintf(buf, sizeof(buf), "%lu", ptr_xauth->serialno); + if (res < 0 || (unsigned int)res >= sizeof(buf)) { + libreswan_log("XAUTH: #%lu: snprintf failure while downing VPN for user '%s'", + ptr_xauth->ptarg.st_serialno, ptr_xauth->ptarg.name); + continue; + } + char *args[] = { + "ipsec", "whack", "--deletestate", buf, NULL + }; + int err = posix_spawnp(&child, "ipsec", NULL, NULL, args, environ); + if (err) { + libreswan_log("XAUTH: #%lu: posix_spawnp failure while downing VPN for user '%s'", + ptr_xauth->ptarg.st_serialno, ptr_xauth->ptarg.name); + continue; + } + libreswan_log("XAUTH: #%lu: sent VPN down command for user '%s'", + ptr_xauth->ptarg.st_serialno, ptr_xauth->ptarg.name); + } + /* Failed pam_open_session */ + } } else if(ptr_xauth->ptarg.pam_do_state == PAM_SESSION_END) { for (int i = 0; i < 5; i++) { diff --git a/programs/pluto/pam_conv.h b/programs/pluto/pam_conv.h index 7ca240b2a10..452bd5d59f2 100644 --- a/programs/pluto/pam_conv.h +++ b/programs/pluto/pam_conv.h @@ -19,6 +19,7 @@ //#include "xauth.h" #include "constants.h" +#include struct state; typedef void xauth_callback_t( @@ -30,11 +31,12 @@ struct app_pam_data { const char* password; }; enum pam_state_t { PAM_AUTH = 0, - PAM_SESSION_START = 1, - PAM_SESSION_END = 2, - PAM_TERM = 3, - PAM_STATE_UNKNOWN = 4, - PAM_DO_NOTHING = 5 + PAM_CALLBACK = 1, + PAM_SESSION_START = 2, + PAM_SESSION_END = 3, + PAM_TERM = 4, + PAM_STATE_UNKNOWN = 5, + PAM_DO_NOTHING = 6 }; @@ -75,6 +77,7 @@ struct xauth { xauth_callback_t *callback; bool abort; pid_t child; + atomic_flag vpn_still_starting; }; extern bool do_pam_authentication(struct pam_thread_arg *arg); diff --git a/programs/pluto/xauth.c b/programs/pluto/xauth.c index 235fd81bf85..24dbd3dcb28 100644 --- a/programs/pluto/xauth.c +++ b/programs/pluto/xauth.c @@ -52,6 +52,7 @@ void xauth_start_pam_thread(struct state *st, xauth->callback = callback; xauth->serialno = serialno; + atomic_flag_test_and_set(&xauth->vpn_still_starting); gettimeofday(&xauth->tv0, NULL); /* fill in pam_thread_arg with info for the child process */