From 041d50dba33f979b128b2ffa6c7ea0639145956d Mon Sep 17 00:00:00 2001 From: Yoshimasa Niwa Date: Sun, 4 Nov 2018 00:53:37 -0700 Subject: [PATCH] Add `--save-to-keychain` option. --- main.c | 48 ++++++++++++++++++++++++++++-------------- openconnect-internal.h | 5 +++++ openconnect.8.in | 14 ++++++++++++ 3 files changed, 51 insertions(+), 16 deletions(-) diff --git a/main.c b/main.c index 2445ce31..195cae71 100644 --- a/main.c +++ b/main.c @@ -91,6 +91,7 @@ static int non_inter; static int cookieonly; static int allow_stdin_read; static char *keychain_account = NULL; +static struct oc_text_list_item *keychain_saving_fields = NULL; static char *token_filename; static char *server_cert = NULL; @@ -178,6 +179,7 @@ enum { OPT_PIDFILE, OPT_PASSWORD_ON_STDIN, OPT_USE_KEYCHAIN, + OPT_SAVE_TO_KEYCHAIN, OPT_PRINTCOOKIE, OPT_RECONNECT_TIMEOUT, OPT_SERVERCERT, @@ -255,6 +257,7 @@ static const struct option long_options[] = { OPTION("passwd-on-stdin", 0, OPT_PASSWORD_ON_STDIN), #if ENABLE_KEYCHAIN OPTION("use-keychain", 1, OPT_USE_KEYCHAIN), + OPTION("save-to-keychain", 1, OPT_SAVE_TO_KEYCHAIN), #endif OPTION("no-passwd", 0, OPT_NO_PASSWD), OPTION("reconnect-timeout", 1, OPT_RECONNECT_TIMEOUT), @@ -810,6 +813,7 @@ static void usage(void) printf(" --passwd-on-stdin %s\n", _("Read password from standard input")); #if ENABLE_KEYCHAIN printf(" --use-keychain=ACCOUNT %s\n", _("Look up Keychain to fill password form fields")); + printf(" --save-to-keychain=NAME %s\n", _("Name of password form field to be saved to Keychain")); #endif printf(" --authgroup=GROUP %s\n", _("Choose authentication login selection")); printf(" -c, --certificate=CERT %s\n", _("Use SSL client certificate CERT")); @@ -1301,6 +1305,13 @@ int main(int argc, char **argv) case OPT_USE_KEYCHAIN: keychain_account = keep_config_arg(); break; + case OPT_SAVE_TO_KEYCHAIN: { + struct oc_text_list_item *field = malloc(sizeof(*field)); + field->data = keep_config_arg(); + field->next = keychain_saving_fields; + keychain_saving_fields = field; + break; + } #endif case OPT_NO_PASSWD: vpninfo->nopasswd = 1; @@ -2007,26 +2018,31 @@ static char *lookup_keychain_password(const char *acc, size_t len = strlen(result); if (len == 0) goto end; - label = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("openconnect: %@ (%@)"), account, name); - if (!label) goto end; - data = CFDataCreate(kCFAllocatorDefault, (UInt8 *)result, len + 1); - if (!data) goto end; + for (struct oc_text_list_item *field = keychain_saving_fields; field; field = field->next) { + if (strcmp(opt->name, field->data)) + continue; - CFDictionaryAddValue(query, kSecAttrLabel, label); - CFDictionaryAddValue(query, kSecValueData, data); - CFDictionaryRemoveValue(query, kSecReturnData); + label = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("openconnect: %@ (%@)"), account, name); + if (!label) goto end; + data = CFDataCreate(kCFAllocatorDefault, (UInt8 *)result, len + 1); + if (!data) goto end; - err = SecItemAdd(query, NULL); - if (err != errSecSuccess) { - if (verbose > PRG_ERR) - fprintf(stderr, "Failed to add item to Keychain error: %d\n", err); - } else { - if (verbose > PRG_INFO) - fprintf(stderr, "Item saved in Keychain\n"); + CFDictionaryAddValue(query, kSecAttrLabel, label); + CFDictionaryAddValue(query, kSecValueData, data); + CFDictionaryRemoveValue(query, kSecReturnData); + + err = SecItemAdd(query, NULL); + if (err != errSecSuccess) { + if (verbose > PRG_ERR) + fprintf(stderr, "Failed to add item to Keychain error: %d\n", err); + } else { + if (verbose > PRG_INFO) + fprintf(stderr, "Item saved in Keychain\n"); + } + goto end; } goto end; - } - if (err != errSecSuccess) { + } else if (err != errSecSuccess) { if (verbose > PRG_ERR) fprintf(stderr, "Failed to find item in Keychain error: %d\n", err); goto end; diff --git a/openconnect-internal.h b/openconnect-internal.h index 8aa8fc89..c6b8686e 100644 --- a/openconnect-internal.h +++ b/openconnect-internal.h @@ -197,6 +197,11 @@ struct oc_text_buf { int error; }; +struct oc_text_list_item { + char *data; + struct oc_text_list_item *next; +}; + #define TLS_MASTER_KEY_SIZE 48 #define RECONNECT_INTERVAL_MIN 10 diff --git a/openconnect.8.in b/openconnect.8.in index 2253f5bb..437d374f 100644 --- a/openconnect.8.in +++ b/openconnect.8.in @@ -58,6 +58,7 @@ openconnect \- Multi-protocol VPN client, for Cisco AnyConnect VPNs and others .OP \-\-non\-inter .OP \-\-passwd\-on\-stdin .OP \-\-use\-keychain string +.OP \-\-save\-to\-keychain string .OP \-\-protocol proto .OP \-\-token\-mode mode .OP \-\-token\-secret {secret\fR[\fI,counter\fR]|@\fIfile\fR} @@ -435,6 +436,19 @@ is a base name of Keychain items. For example, if is "companyvpn", it looks up Keychain item named "companyvpn:token" for "token" password form field. .TP +.B \-\-save\-to\-keychain=NAME +Name of password form field to be saved to Keychain. +.I \-\-use\-keychain +option is required. +For example, if +.I \-\-use\-keychain +options's +.I ACCOUNT +is "companyvpn" and +.I NAME +is "token", it saves input value to Keychain item named "companyvpn:token" for +"token" password form field. +.TP .B \-\-protocol=PROTO Select VPN protocol .I PROTO -- 2.49.0