diff --git a/cvpn b/cvpn index 21a4c35..f3aa0d9 100755 --- a/cvpn +++ b/cvpn @@ -15,7 +15,15 @@ VALID_VENDORS="cisco|f5" cp -p "$LOGFILE" "${LOGFILE}.0" cp /dev/null "$LOGFILE" -VPN=/opt/cisco/anyconnect/bin/vpn +OPENCONNECT=$(which openconnect 2>/dev/null) +if [[ $? -ne 0 ]]; then + warn "Can't find openconnect binary - Cisco VPNs won't work." +fi +VPNSLICE=$(which vpn-slice 2>/dev/null) +if [[ $? -ne 0 ]]; then + warn "Can't find vpn-slice binary - Cisco VPNs won't work." +fi + mkdir -p "${CONFDIR}" @@ -41,7 +49,7 @@ function alias_to_cmd() { } function load_config() { - local CONFLINES line thisprofile thisuser thispw thisgroup + local CONFLINES line thisprofile thisuser thispw thisgroup thisroutes thisservercert DEFAULTPROFILE="" if [[ -e ${CONFFILE} ]]; then DEFAULTPROFILE=$(grep '^#default:' "${CONFFILE}" | sed 's/^#default://') @@ -55,6 +63,8 @@ function load_config() { thisgroup=$(cut -d, -f4 <<< "${line}") thisvendor=$(cut -d, -f5 <<< "${line}") thisserver=$(cut -d, -f6 <<< "${line}") + thisroutes=$(cut -d, -f7 <<< "${line}") + thisservercert=$(cut -d, -f8 <<< "${line}") [[ -z $thisvendor ]] && thisvendor="cisco" [[ -z $thisserver ]] && thisvendor="n/a" if [[ ! $thisvendor =~ $VALID_VENDORS ]]; then @@ -68,6 +78,8 @@ function load_config() { group[$nvpns]="${thisgroup}" vendor[$nvpns]="${thisvendor}" server[$nvpns]="${thisserver}" + routes[$nvpns]="${thisroutes}" + servercert[$nvpns]="${thisservercert}" nvpns=$((nvpns + 1)) done <<< "${CONFLINES}" else @@ -128,19 +140,14 @@ function get_vpn_status() { # populates vprofile vuser vstatus vserver vvendor, vserver=$(echo "$pname" | sed 's/^.*--server //;s/ .*//g') vuser=$(echo "$pname" | sed 's/^.*--username //;s/ .*//g') vvendor=F5 + elif [[ $pname =~ openconnect ]]; then + vstatus="Connected" + vserver=$(echo "$pname" | awk '{ print $NF }') + vuser=$(echo "$pname" | sed 's/^.*-u //;s/ .*//g') + vvendor=Cisco fi else - # no f5 vpn, check anyconnect - res=$(${VPN} stats 2>&1) - if [[ $? -eq 0 ]]; then - vstatus=$(grep 'Connection State:' <<< "$res" | grep -v Manage | awk '{ print $3 }' ) - if [[ $vstatus == "Connected" ]]; then - vvendor="Cisco" - vserver=$(grep 'Server Address:' <<< "$res" | awk '{ print $3 }' ) - fi - else - rv=1 - fi + rv=1 fi if [[ $vstatus == "Connected" ]]; then @@ -232,25 +239,69 @@ elif [[ $cmd == "on" ]]; then fi id=$(get_profile_id "${PROFILE}") if [[ -n $id ]]; then + curprofile=${profile[$id]} curuser=${user[$id]} curpw=${pw[$id]} curgroup=${group[$id]} curvendor=${vendor[$id]} - if [[ $curvendor == "f5" ]]; then - curserver=${server[$id]} - else - curserver="" - fi + curserver=${server[$id]} + curservercert=${servercert[$id]} + curroutes=${routes[$id]} else + error "Could not determine VPN ID for profile ^b${PROFILE}^p." exit 1 fi + if [[ -z $curservercert && ${curvendor,,} == "cisco" ]]; then + inform "No server certificate is defined for ^b$curprofile^p." + notify "Trying to obtain server certificate" + foundcert=$(sudo ${OPENCONNECT} --non-inter --protocol=anyconnect -u "$curuser" --authgroup="$curgroup" "$curserver" 2>&1 | grep -- --servercert | awk '{ print $NF }') + if [[ -n $foundcert ]]; then + ok + inform "Got server certificate '^b${foundcert}^p'" + notify "Updating configuration file ^b$CONFFILE^p" + bakfile="${CONFFILE}".backup + prevlines=$(cat "$CONFFILE" | awk NF | wc -l | bc) + newconfig=$(cat "${CONFFILE}" | awk -v p="$curprofile" -v c="$foundcert" -F, '{ OFS=","; if ($1 == p) { $8 = c; } print }') + newlines=$(echo "$newconfig" | awk NF | wc -l | bc) + + err="" + if [[ $newlines -ne $prevlines ]]; then + err="line count different" + elif ! grep -q pin-sha256 <<<"$newconfig"; then + err="new config missing server cert" + fi + + if [[ -z $err ]]; then + ok + cp -p "${CONFFILE}" "${bakfile}" + echo "$newconfig" > ${CONFFILE} + curservercert="$foundcert" + else + fail + error "Regenerated config file seems wrong ($err)" + cecho -s "$RED" "Current config:" + cecho -s "$RED" "$(cat "$CONFFILE")" | sed 's/^/ /' + echo + cecho -s "$RED" "Newly generated config:" + cecho -s "$RED" "$newconfig" | sed 's/^/ /' + echo + exit 1 + fi + else + fail + exit 1 + fi + fi + nstr="Connecting to ^b${PROFILE}^p as user ^b${curuser}^p" [[ -n $curgroup ]] && nstr="${nstr} group ${curgroup}" + [[ -n $curserver ]] && nstr="${nstr} server ${curserver}" + notify "${nstr}" + rm -f "$VPNPIDFILE" if [[ $curvendor == "f5" ]]; then - rm -f "$VPNPIDFILE" nohup sudo gof5 --server "${curserver}" --username "${curuser}" --password "${curpw}" >>${LOGFILE} 2>&1 & f5pid=$! nlines=0 @@ -286,25 +337,28 @@ elif [[ $cmd == "on" ]]; then echo "$f5pid" > "$VPNPIDFILE" fi else - if [[ -n $curgroup ]]; then - answers="y\n${curgroup}\n${curuser}\n${curpw}\n" + if [[ -n $curroutes ]]; then + printf '%s' "$curpw" | sudo ${OPENCONNECT} --background --non-inter --protocol=anyconnect -u "$curuser" --passwd-on-stdin --authgroup="$curgroup" -s "$VPNSLICE $routesarg" --servercert "$curservercert" "$curserver" >"${LOGFILE}" 2>&1 else - answers="y\n${curuser}\n${curpw}\n" + printf '%s' "$curpw" | sudo ${OPENCONNECT} --background --non-inter --protocol=anyconnect -u "$curuser" --passwd-on-stdin --authgroup="$curgroup" --servercert "$curservercert" "$curserver" >"${LOGFILE}" 2>&1 fi - res=$(echo -e "$answers" | ${VPN} -s connect "$PROFILE" 2>&1 | tee ${LOGFILE}) - grep -q 'state: Connected' <<< "$res$" rv=$? + res=$(cat "$LOGFILE") + if [[ $rv -eq 0 ]]; then + cpid=$(pgrep openconnect) + if [[ $? -eq 0 ]]; then + echo "$cpid" > "$VPNPIDFILE" + else + rv=1 + fi + fi fi if [[ $rv -eq 0 ]]; then ok "connected" else fail - if [[ $curvendor == "cisco" ]]; then - error "Connection failed, logs are in ^b${LOGFILE}^p." - else - error "Connection failed, logs are below:" - cecho -s "$RED" "$res" | sed 's/^/ /' - fi + error "Connection failed, logs are below:" + cecho -s "$RED" "$res" | sed 's/^/ /' fi elif [[ $cmd == "status" ]]; then BASECOL="$ORANGE" @@ -325,21 +379,33 @@ elif [[ $cmd == "off" ]]; then get_vpn_status if [[ $vstatus == "Connected" ]]; then notify "Disconnecting from ${vvendor} VPN ^b${vprofile}^p" + errstring="" + errdata="" - if [[ ${vvendor,,} == "f5" ]]; then - #sudo gof5 --server ${s} --close-session - sudo kill $(cat $VPNPIDFILE) - rm -f "${VPNPIDFILE}" - rv=0 + thepid=$(cat $VPNPIDFILE) + if [[ -n $thepid ]]; then + sudo kill $thepid + sleep 2 # give it some time + res=$(ps -p $thepid 2>&1) + if [[ $? -ne 0 ]]; then + rm -f "${VPNPIDFILE}" + rv=0 + else + errstring="could not kill pid $thepid:" + errdata="$res" + rv=1 + fi else - ${VPN} disconnect > ${LOGFILE} 2>&1 - rv=$? + errstring="Cannot determine current VPN PID to kill." + rv=1 fi + if [[ $rv -eq 0 ]]; then ok else fail - error "Connection failed, logs are in ^b${LOGFILE}^p." + error "$errstring" + [[ -n $errdata ]] && cecho -s "$RED" "$errdata" | sed 's/^/ /' fi else fail