openconnect: add netifd support
authorFelix Fietkau <nbd@openwrt.org>
Mon, 14 May 2012 20:58:41 +0000 (20:58 +0000)
committerFelix Fietkau <nbd@openwrt.org>
Mon, 14 May 2012 20:58:41 +0000 (20:58 +0000)
SVN-Revision: 31729

net/openconnect/Makefile
net/openconnect/files.old/openconnect.sh [new file with mode: 0755]
net/openconnect/files.old/run-openconnect [new file with mode: 0755]
net/openconnect/files/openconnect.sh
net/openconnect/files/run-openconnect [deleted file]
net/openconnect/files/vpnc-script [new file with mode: 0755]
net/openconnect/patches/100-passwd_file.patch [new file with mode: 0644]

index 9ba580c50f885597406158da97878ca808453886..1e1ba66aaaaf5bf7aa9bca8682a78376cc3553e7 100644 (file)
@@ -20,7 +20,7 @@ include $(INCLUDE_DIR)/package.mk
 define Package/openconnect
   SECTION:=net
   CATEGORY:=Network
-  DEPENDS:=+libxml2 +libopenssl +kmod-tun +vpnc-scripts
+  DEPENDS:=+libxml2 +libopenssl +kmod-tun +resolveip +!PACKAGE_netifd:vpnc-scripts
   TITLE:=VPN client for Cisco's AnyConnect SSL VPN
   URL:=http://www.infradead.org/openconnect/
   SUBMENU:=VPN
@@ -35,13 +35,26 @@ define Package/openconnect/description
 endef
 
 CONFIGURE_ARGS+=--disable-shared
+ifdef CONFIG_PACKAGE_netifd
+  CONFIGURE_ARGS += --with-vpnc-script=/lib/netifd/vpnc-script
 
-define Package/openconnect/install
+  define Package/openconnect/install
+       $(INSTALL_DIR) $(1)/lib/netifd/proto
+       $(INSTALL_BIN) ./files/openconnect.sh $(1)/lib/netifd/proto/
+       $(INSTALL_BIN) ./files/vpnc-script $(1)/lib/netifd/
+       $(INSTALL_DIR) $(1)/usr/sbin
+       $(INSTALL_BIN) $(PKG_BUILD_DIR)/openconnect $(1)/usr/sbin/
+  endef
+else
+  CONFIGURE_ARGS += --with-vpnc-script=/etc/vpnc/vpnc-script
+
+  define Package/openconnect/install
        $(INSTALL_DIR) $(1)/lib/network
-       $(INSTALL_BIN) ./files/openconnect.sh $(1)/lib/network/
+       $(INSTALL_BIN) ./files.old/openconnect.sh $(1)/lib/network/
        $(INSTALL_DIR) $(1)/usr/sbin
-       $(INSTALL_BIN) ./files/run-openconnect $(1)/usr/sbin/
+       $(INSTALL_BIN) ./files.old/run-openconnect $(1)/usr/sbin/
        $(INSTALL_BIN) $(PKG_BUILD_DIR)/openconnect $(1)/usr/sbin/
-endef
+  endef
+endif
 
 $(eval $(call BuildPackage,openconnect))
diff --git a/net/openconnect/files.old/openconnect.sh b/net/openconnect/files.old/openconnect.sh
new file mode 100755 (executable)
index 0000000..13221ab
--- /dev/null
@@ -0,0 +1,39 @@
+find_gw() {
+       route -n | awk '$1 == "0.0.0.0" { print $2; exit }'
+}
+
+scan_openconnect() {
+       config_set "$1" device "vpn-$1"
+}
+
+stop_interface_openconnect() {
+       local config="$1"
+       local lock="/var/lock/openconnect-$config"
+
+        uci_set_state network "$config" up 0
+
+       lock "$lock"
+
+       SERVICE_PID_FILE="/var/run/openconnect-${config}.pid" \
+         SERVICE_SIG=HUP service_stop /bin/sh
+
+       remove_dns "$config"
+
+       lock -u "$lock"
+}
+
+setup_interface_openconnect() {
+       local config="$2"
+
+       /sbin/insmod tun 2>&- >&-
+
+       # creating the tunnel below will trigger a net subsystem event
+        # prevent it from touching or iface by disabling .auto here
+        uci_set_state network "$config" ifname "vpn-$config"
+        uci_set_state network "$config" auto 0
+        uci_set_state network "$config" up 1
+
+       SERVICE_PID_FILE="/var/run/openconnect-${config}.pid" \
+          SERVICE_WRITE_PID=1  SERVICE_DAEMONIZE=1 \
+        service_start /usr/sbin/run-openconnect $config
+}
diff --git a/net/openconnect/files.old/run-openconnect b/net/openconnect/files.old/run-openconnect
new file mode 100755 (executable)
index 0000000..e835891
--- /dev/null
@@ -0,0 +1,86 @@
+#!/bin/sh -x
+
+. /etc/functions.sh
+
+[ $# = 0 ] && { echo "  $0 <group>"; exit; }
+
+include /lib/network
+scan_interfaces
+
+config="$1"
+export OPENWRT_INTERFACE="$config"
+
+config_get proto "$config" proto
+
+if [ "$proto" != "openconnect" ]; then
+    echo "Interface $config is $proto not openconnect" >&2
+    exit 1
+fi
+
+config_get device "$config" device
+
+local server
+config_get server "$config" server
+
+local port
+config_get port "$config" port
+if [ -n "$port" ]; then
+    args="$server:$port"
+else
+    args="$server"
+fi
+
+local cookie
+config_get cookie "$config" cookie
+[ -n "$cookie" ] && args="$args -C $cookie"
+
+local username
+config_get username "$config" username
+[ -n "$username" ] && args="$args -u $username"
+
+local password
+config_get password "$password" password
+
+/sbin/insmod tun
+
+local lock="/var/lock/openconnect-$config"
+
+# creating the tunnel below will trigger a net subsystem event
+# prevent it from touching or iface by disabling .auto here
+uci_set_state network "$config" ifname $link
+uci_set_state network "$config" auto 0
+
+local gw="$(find_gw)"
+[ -n "$gw" ] && {
+    local serv_addrs=""
+    for ip in $(resolveip -4 -t 3 "$server"); do
+       append serv_addrs "$ip"
+       route delete -host "$ip" 2>/dev/null
+       route add -host "$ip" gw "$gw"
+    done
+    uci_toggle_state network "$config" serv_addrs "$serv_addrs"
+}
+
+RECON=$(date +%s)
+
+trap "[ -r /var/run/openconnect-$config-oc.pid ] && kill -HUP \$(cat /var/run/openconnect-$config-oc.pid)" SIGHUP
+while [ "$(uci_get_state network ${config} up)" = "1" ]; do
+    NOW=$(date +%s)
+    if [ $RECON -gt $NOW ]; then
+       DELAY=$(expr $RECON - $NOW)
+       logger -t openconnect "Waiting for $DELAY seconds before reconnecting"
+       sleep $(expr $DELAY)
+    fi
+
+    # The lock prevents a race condition where /lib/network/openconnect.sh could
+    # send us SIGHUP after we spawn openconnect, but before we store its pid.
+    # Thus leaving it running after we should have killed it.
+    lock $lock
+    echo "$passwd" | /usr/sbin/openconnect $args -i "vpn-$config" \
+       --no-cert-check --non-inter --passwd-on-stdin --syslog --script /etc/vpnc/vpnc-script &
+    echo $! > /var/run/openconnect-$config-oc.pid
+    lock -u $lock
+    wait $!
+    rm /var/run/openconnect-$config-oc.pid
+    RECON=$(expr $NOW + 60)
+done
index 13221abaf8dffd69e40d89ce221d51608719f686..f99b34ea13c6116a375690c4d5899790c5d8a783 100755 (executable)
@@ -1,39 +1,56 @@
-find_gw() {
-       route -n | awk '$1 == "0.0.0.0" { print $2; exit }'
+#!/bin/sh
+. /etc/functions.sh
+. ../netifd-proto.sh
+init_proto "$@"
+
+proto_openconnect_init_config() {
+       proto_config_add_string "server"
+       proto_config_add_int "port"
+       proto_config_add_string "username"
+       proto_config_add_string "cookie"
+       proto_config_add_string "password"
+       no_device=1
+       available=1
 }
 
-scan_openconnect() {
-       config_set "$1" device "vpn-$1"
-}
-
-stop_interface_openconnect() {
+proto_openconnect_setup() {
        local config="$1"
-       local lock="/var/lock/openconnect-$config"
-
-        uci_set_state network "$config" up 0
-
-       lock "$lock"
-
-       SERVICE_PID_FILE="/var/run/openconnect-${config}.pid" \
-         SERVICE_SIG=HUP service_stop /bin/sh
 
-       remove_dns "$config"
-
-       lock -u "$lock"
+       json_get_vars server port username cookie password
+
+       grep -q tun /proc/modules || insmod tun
+
+       serv_addr=
+       for ip in $(resolveip -t 5 "$server"); do
+               proto_add_host_dependency "$config" "$server"
+               serv_addr=1
+       done
+       [ -n "$serv_addr" ] || {
+               echo "Could not resolve server address"
+               sleep 5
+               proto_setup_failed "$config"
+               exit 1
+       }
+
+       [ -n "$port" ] && port=":$port"
+
+       cmdline="$server$port -i vpn-$config --no-cert-check --non-inter --syslog --script /lib/netifd/vpnc-script"
+
+       [ -n "$cookie" ] && append cmdline "-C $cookie"
+       [ -n "$username" ] && append cmdline "-u $username"
+       [ -n "$password" ] && {
+               umask 077
+               pwfile="/var/run/openconnect-$config.passwd"
+               echo "$password" > "$pwfile"
+               append cmdline "--passwd-file=$pwfile"
+       }
+
+       proto_export INTERFACE="$config"
+       proto_run_command "$config" /usr/sbin/openconnect $cmdline
 }
 
-setup_interface_openconnect() {
-       local config="$2"
-
-       /sbin/insmod tun 2>&- >&-
-
-       # creating the tunnel below will trigger a net subsystem event
-        # prevent it from touching or iface by disabling .auto here
-        uci_set_state network "$config" ifname "vpn-$config"
-        uci_set_state network "$config" auto 0
-        uci_set_state network "$config" up 1
-
-       SERVICE_PID_FILE="/var/run/openconnect-${config}.pid" \
-          SERVICE_WRITE_PID=1  SERVICE_DAEMONIZE=1 \
-        service_start /usr/sbin/run-openconnect $config
+proto_openconnect_teardown() {
+       proto_kill_command "$config"
 }
+
+add_protocol openconnect
diff --git a/net/openconnect/files/run-openconnect b/net/openconnect/files/run-openconnect
deleted file mode 100755 (executable)
index e835891..0000000
+++ /dev/null
@@ -1,86 +0,0 @@
-#!/bin/sh -x
-
-. /etc/functions.sh
-
-[ $# = 0 ] && { echo "  $0 <group>"; exit; }
-
-include /lib/network
-scan_interfaces
-
-config="$1"
-export OPENWRT_INTERFACE="$config"
-
-config_get proto "$config" proto
-
-if [ "$proto" != "openconnect" ]; then
-    echo "Interface $config is $proto not openconnect" >&2
-    exit 1
-fi
-
-config_get device "$config" device
-
-local server
-config_get server "$config" server
-
-local port
-config_get port "$config" port
-if [ -n "$port" ]; then
-    args="$server:$port"
-else
-    args="$server"
-fi
-
-local cookie
-config_get cookie "$config" cookie
-[ -n "$cookie" ] && args="$args -C $cookie"
-
-local username
-config_get username "$config" username
-[ -n "$username" ] && args="$args -u $username"
-
-local password
-config_get password "$password" password
-
-/sbin/insmod tun
-
-local lock="/var/lock/openconnect-$config"
-
-# creating the tunnel below will trigger a net subsystem event
-# prevent it from touching or iface by disabling .auto here
-uci_set_state network "$config" ifname $link
-uci_set_state network "$config" auto 0
-
-local gw="$(find_gw)"
-[ -n "$gw" ] && {
-    local serv_addrs=""
-    for ip in $(resolveip -4 -t 3 "$server"); do
-       append serv_addrs "$ip"
-       route delete -host "$ip" 2>/dev/null
-       route add -host "$ip" gw "$gw"
-    done
-    uci_toggle_state network "$config" serv_addrs "$serv_addrs"
-}
-
-RECON=$(date +%s)
-
-trap "[ -r /var/run/openconnect-$config-oc.pid ] && kill -HUP \$(cat /var/run/openconnect-$config-oc.pid)" SIGHUP
-while [ "$(uci_get_state network ${config} up)" = "1" ]; do
-    NOW=$(date +%s)
-    if [ $RECON -gt $NOW ]; then
-       DELAY=$(expr $RECON - $NOW)
-       logger -t openconnect "Waiting for $DELAY seconds before reconnecting"
-       sleep $(expr $DELAY)
-    fi
-
-    # The lock prevents a race condition where /lib/network/openconnect.sh could
-    # send us SIGHUP after we spawn openconnect, but before we store its pid.
-    # Thus leaving it running after we should have killed it.
-    lock $lock
-    echo "$passwd" | /usr/sbin/openconnect $args -i "vpn-$config" \
-       --no-cert-check --non-inter --passwd-on-stdin --syslog --script /etc/vpnc/vpnc-script &
-    echo $! > /var/run/openconnect-$config-oc.pid
-    lock -u $lock
-    wait $!
-    rm /var/run/openconnect-$config-oc.pid
-    RECON=$(expr $NOW + 60)
-done
diff --git a/net/openconnect/files/vpnc-script b/net/openconnect/files/vpnc-script
new file mode 100755 (executable)
index 0000000..4d12d7e
--- /dev/null
@@ -0,0 +1,156 @@
+#!/bin/sh
+# List of parameters passed through environment
+#* reason                       -- why this script was called, one of: pre-init connect disconnect
+#* VPNGATEWAY                   -- vpn gateway address (always present)
+#* TUNDEV                       -- tunnel device (always present)
+#* INTERNAL_IP4_ADDRESS         -- address (always present)
+#* INTERNAL_IP4_MTU             -- mtu (often unset)
+#* INTERNAL_IP4_NETMASK         -- netmask (often unset)
+#* INTERNAL_IP4_NETMASKLEN      -- netmask length (often unset)
+#* INTERNAL_IP4_NETADDR         -- address of network (only present if netmask is set)
+#* INTERNAL_IP4_DNS             -- list of dns servers
+#* INTERNAL_IP4_NBNS            -- list of wins servers
+#* INTERNAL_IP6_ADDRESS         -- IPv6 address
+#* INTERNAL_IP6_NETMASK         -- IPv6 netmask
+#* INTERNAL_IP6_DNS             -- IPv6 list of dns servers
+#* CISCO_DEF_DOMAIN             -- default domain name
+#* CISCO_BANNER                 -- banner from server
+#* CISCO_SPLIT_INC              -- number of networks in split-network-list
+#* CISCO_SPLIT_INC_%d_ADDR      -- network address
+#* CISCO_SPLIT_INC_%d_MASK      -- subnet mask (for example: 255.255.255.0)
+#* CISCO_SPLIT_INC_%d_MASKLEN   -- subnet masklen (for example: 24)
+#* CISCO_SPLIT_INC_%d_PROTOCOL  -- protocol (often just 0)
+#* CISCO_SPLIT_INC_%d_SPORT     -- source port (often just 0)
+#* CISCO_SPLIT_INC_%d_DPORT     -- destination port (often just 0)
+#* CISCO_IPV6_SPLIT_INC         -- number of networks in IPv6 split-network-list
+#* CISCO_IPV6_SPLIT_INC_%d_ADDR -- IPv6 network address
+#* CISCO_IPV6_SPLIT_INC_$%d_MASKLEN -- IPv6 subnet masklen
+
+# FIXMEs:
+
+# Section A: route handling
+
+# 1) The 3 values CISCO_SPLIT_INC_%d_PROTOCOL/SPORT/DPORT are currently being ignored
+#   In order to use them, we'll probably need os specific solutions
+#   * Linux: iptables -t mangle -I PREROUTING <conditions> -j ROUTE --oif $TUNDEV
+#       This would be an *alternative* to changing the routes (and thus 2) and 3)
+#       shouldn't be relevant at all)
+# 2) There are two different functions to set routes: generic routes and the
+#   default route. Why isn't the defaultroute handled via the generic route case?
+# 3) In the split tunnel case, all routes but the default route might get replaced
+#   without getting restored later. We should explicitely check and save them just
+#   like the defaultroute
+# 4) Replies to a dhcp-server should never be sent into the tunnel
+
+# Section B: Split DNS handling
+
+# 1) Maybe dnsmasq can do something like that
+# 2) Parse dns packets going out via tunnel and redirect them to original dns-server
+
+do_connect() {
+       if [ -n "$CISCO_BANNER" ]; then
+               echo "Connect Banner:"
+               echo "$CISCO_BANNER" | while read LINE ; do echo "|" "$LINE" ; done
+               echo
+       fi
+
+       proto_init_update "$TUNDEV" 1
+
+       if [ -n "$INTERNAL_IP4_MTU" ]; then
+               MTU=$INTERNAL_IP4_MTU
+       fi
+
+       if [ -z "$MTU" ]; then
+               MTU=1412
+       fi
+
+       proto_add_ipv4_address "$INTERNAL_IP4_ADDRESS" 32 "" "$INTERNAL_IP4_ADDRESS"
+
+       if [ -n "$INTERNAL_IP4_NETMASKLEN" ]; then
+               proto_add_ipv4_route "$INTERNAL_IP4_NETADDR" "$INTERNAL_IP4_NETMASKLEN"
+       fi
+
+       # If the netmask is provided, it contains the address _and_ netmask
+       if [ -n "$INTERNAL_IP6_ADDRESS" ] && [ -z "$INTERNAL_IP6_NETMASK" ]; then
+           INTERNAL_IP6_NETMASK="$INTERNAL_IP6_ADDRESS/128"
+       fi
+
+       if [ -n "$INTERNAL_IP6_NETMASK" ]; then
+               addr="${INTERNAL_IP6_NETMASK%%/*}"
+               mask="${INTERNAL_IP6_NETMASK##*/}"
+               [[ "$addr" != "$mask" ]] && proto_add_ipv6_address "$addr" "$mask"
+       fi
+
+       [ -n "$INTERNAL_IP4_DNS" ] && proto_add_dns_server "$INTERNAL_IP4_DNS"
+       [ -n "$CISCO_DEF_DOMAIN" ] && proto_add_dns_search "$CISCO_DEF_DOMAIN"
+
+       if [ -n "$CISCO_SPLIT_INC" ]; then
+               i=0
+               while [ $i -lt $CISCO_SPLIT_INC ] ; do
+                       eval NETWORK="\${CISCO_SPLIT_INC_${i}_ADDR}"
+                       eval NETMASK="\${CISCO_SPLIT_INC_${i}_MASK}"
+                       eval NETMASKLEN="\${CISCO_SPLIT_INC_${i}_MASKLEN}"
+                       if [ $NETWORK != "0.0.0.0" ]; then
+                               proto_add_ipv4_route "$NETWORK" "$NETMASKLEN"
+                       else
+                               proto_add_ipv4_route "0.0.0.0" 0
+                       fi
+                       i=$(($i + 1))
+               done
+       elif [ -n "$INTERNAL_IP4_ADDRESS" ]; then
+               proto_add_ipv4_route "0.0.0.0" 0
+       fi
+       if [ -n "$CISCO_IPV6_SPLIT_INC" ]; then
+               i=0
+               while [ $i -lt $CISCO_IPV6_SPLIT_INC ] ; do
+                       eval NETWORK="\${CISCO_IPV6_SPLIT_INC_${i}_ADDR}"
+                       eval NETMASKLEN="\${CISCO_IPV6_SPLIT_INC_${i}_MASKLEN}"
+                       if [ $NETMASKLEN -lt 128 ]; then
+                               proto_add_ipv6_route "$NETWORK" "$NETMASKLEN"
+                       else
+                               proto_add_ipv6_route "::0" 0
+                       fi
+                       i=$(($i + 1))
+               done
+       elif [ -n "$INTERNAL_IP6_NETMASK" -o -n "$INTERNAL_IP6_ADDRESS" ]; then
+               proto_add_ipv6_route "::0" 0
+       fi
+       proto_send_update "$INTERFACE"
+}
+
+do_disconnect() {
+       proto_init_update "$TUNDEV" 0
+       proto_send_update "$INTERFACE"
+}
+
+#### Main
+
+if [ -z "$reason" ]; then
+       echo "this script must be called from vpnc" 1>&2
+       exit 1
+fi
+if [ -z "$INTERFACE" ]; then
+       echo "this script must be called for an active interface"
+       exit 1
+fi
+
+. /lib/netifd/netifd-proto.sh
+
+case "$reason" in
+       pre-init)
+               ;;
+       connect)
+               do_connect
+               ;;
+       disconnect)
+               do_disconnect
+               ;;
+       reconnect)
+               ;;
+       *)
+               echo "unknown reason '$reason'. Maybe vpnc-script is out of date" 1>&2
+               exit 1
+               ;;
+esac
+
+exit 0
diff --git a/net/openconnect/patches/100-passwd_file.patch b/net/openconnect/patches/100-passwd_file.patch
new file mode 100644 (file)
index 0000000..3e074d5
--- /dev/null
@@ -0,0 +1,100 @@
+--- a/main.c
++++ b/main.c
+@@ -77,6 +77,7 @@ enum {
+       OPT_CAFILE,
+       OPT_COOKIEONLY,
+       OPT_COOKIE_ON_STDIN,
++      OPT_COOKIE_FILE,
+       OPT_CSD_USER,
+       OPT_CSD_WRAPPER,
+       OPT_DISABLE_IPV6,
+@@ -91,6 +92,7 @@ enum {
+       OPT_NO_PROXY,
+       OPT_PIDFILE,
+       OPT_PASSWORD_ON_STDIN,
++      OPT_PASSWORD_FILE,
+       OPT_PRINTCOOKIE,
+       OPT_RECONNECT_TIMEOUT,
+       OPT_SERVERCERT,
+@@ -139,7 +141,9 @@ static struct option long_options[] = {
+       OPTION("queue-len", 1, 'Q'),
+       OPTION("xmlconfig", 1, 'x'),
+       OPTION("cookie-on-stdin", 0, OPT_COOKIE_ON_STDIN),
++      OPTION("cookie-file", 1, OPT_COOKIE_FILE),
+       OPTION("passwd-on-stdin", 0, OPT_PASSWORD_ON_STDIN),
++      OPTION("passwd-file", 1, OPT_PASSWORD_FILE),
+       OPTION("no-passwd", 0, OPT_NO_PASSWD),
+       OPTION("reconnect-timeout", 1, OPT_RECONNECT_TIMEOUT),
+       OPTION("dtls-ciphers", 1, OPT_DTLS_CIPHERS),
+@@ -177,6 +181,7 @@ static void usage(void)
+       printf("  -K, --key-type=TYPE             %s\n", _("Private key type (PKCS#12 / TPM / PEM)"));
+       printf("  -C, --cookie=COOKIE             %s\n", _("Use WebVPN cookie COOKIE"));
+       printf("      --cookie-on-stdin           %s\n", _("Read cookie from standard input"));
++      printf("      --cookie-file=FILE          %s\n", _("Read cookie from a file"));
+       printf("  -d, --deflate                   %s\n", _("Enable compression (default)"));
+       printf("  -D, --no-deflate                %s\n", _("Disable compression"));
+       printf("      --force-dpd=INTERVAL        %s\n", _("Set minimum Dead Peer Detection interval"));
+@@ -217,6 +222,7 @@ static void usage(void)
+       printf("      --no-cert-check             %s\n", _("Do not require server SSL cert to be valid"));
+       printf("      --non-inter                 %s\n", _("Do not expect user input; exit if it is required"));
+       printf("      --passwd-on-stdin           %s\n", _("Read password from standard input"));
++      printf("      --passwd-file=FILE          %s\n", _("Read password from a file"));
+       printf("      --reconnect-timeout         %s\n", _("Connection retry timeout in seconds"));
+       printf("      --servercert=FINGERPRINT    %s\n", _("Server's certificate SHA1 fingerprint"));
+       printf("      --useragent=STRING          %s\n", _("HTTP header User-Agent: field"));
+@@ -226,15 +232,28 @@ static void usage(void)
+       exit(1);
+ }
+-static void read_stdin(char **string)
++static void read_file(const char *file, char **string)
+ {
+       char *c = malloc(100);
++      FILE *f;
++
++      if (file) {
++              f = fopen(file, "r");
++              if (!f) {
++                      fprintf(stderr, _("Failed to open password file\n"));
++                      exit(1);
++              }
++      } else {
++              file = "stdin";
++              f = stdin;
++      }
++
+       if (!c) {
+-              fprintf(stderr, _("Allocation failure for string from stdin\n"));
++              fprintf(stderr, _("Allocation failure for string from %s\n"), file);
+               exit(1);
+       }
+-      if (!fgets(c, 100, stdin)) {
+-              perror(_("fgets (stdin)"));
++      if (!fgets(c, 100, f)) {
++              perror(_("fgets"));
+               exit(1);
+       }
+@@ -332,14 +351,20 @@ int main(int argc, char **argv)
+                       cookieonly = 2;
+                       break;
+               case OPT_COOKIE_ON_STDIN:
+-                      read_stdin(&vpninfo->cookie);
++                      optarg = NULL;
++                      /* fall through */
++              case OPT_COOKIE_FILE:
++                      read_file(optarg, &vpninfo->cookie);
+                       /* If the cookie is empty, ignore it */
+                       if (! *vpninfo->cookie) {
+                               vpninfo->cookie = NULL;
+                       }
+                       break;
+               case OPT_PASSWORD_ON_STDIN:
+-                      read_stdin(&vpninfo->password);
++                      optarg = NULL;
++                      /* fall through */
++              case OPT_PASSWORD_FILE:
++                      read_file(optarg, &vpninfo->password);
+                       break;
+               case OPT_NO_PASSWD:
+                       vpninfo->nopasswd = 1;