{"thread":{"id":"28393","subject":"[PATCH] contrib: add a pair of credential helpers for Mac OS X's keychain","startedAt":"2011-09-15T02:51:53Z","lastAt":"2011-10-04T20:09:26Z","messageCount":19,"participants":["Jay Soffian","Jeff King","Chris Mear","John Szakmeister","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"175542","messageId":"1316055113-2353-1-git-send-email-jaysoffian@gmail.com","threadId":"28393","inReplyTo":null,"subject":"[PATCH] contrib: add a pair of credential helpers for Mac OS X's keychain","fromName":"Jay Soffian","fromEmail":"jaysoffian@gmail.com","sentAt":"2011-09-15T02:51:53Z","receivedAt":"2011-09-15T02:51:53Z","isPatch":true,"sender":{"key":"jaysoffian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/155970?v=4"},"body":"This credential helper adds, searches, and removes entries from\nthe Mac OS X keychain. The C version links against the Security\nframework and is probably the best choice for daily use.\n\nA python version is also included primarily as a more readable\nexample and uses the /usr/bin/security CLI to access the keychain.\n\nTested with 10.6.8.\n\nSigned-off-by: Jay Soffian <jaysoffian@gmail.com>\n---\nHere's a C version that no longer links to git. I also kept the original\nPython version as an example. I decided not to call out to\n'git credential-gitpass' as it was simple enough to manage /dev/tty\nand there's no portability issues since this is OS X specific.\n\n contrib/credential-osxkeychain/Makefile            |   14 +\n .../git-credential-osxkeychain.c                   |  300 ++++++++++++++++++++\n .../git-credential-osxkeychain.py                  |  148 ++++++++++\n 3 files changed, 462 insertions(+), 0 deletions(-)\n create mode 100644 contrib/credential-osxkeychain/Makefile\n create mode 100644 contrib/credential-osxkeychain/git-credential-osxkeychain.c\n create mode 100755 contrib/credential-osxkeychain/git-credential-osxkeychain.py\n\ndiff --git a/contrib/credential-osxkeychain/Makefile b/contrib/credential-osxkeychain/Makefile\nnew file mode 100644\nindex 0000000000..a0a7074cc6\n--- /dev/null\n+++ b/contrib/credential-osxkeychain/Makefile\n@@ -0,0 +1,14 @@\n+all:: git-credential-osxkeychain\n+\n+CC = gcc\n+RM = rm -f\n+CFLAGS = -O2 -Wall\n+\n+git-credential-osxkeychain: git-credential-osxkeychain.o\n+\t$(CC) -o $@ $< -Wl,-framework -Wl,Security\n+\n+git-credential-osxkeychain.o: git-credential-osxkeychain.c\n+\t$(CC) -c $(CFLAGS) $<\n+\n+clean:\n+\t$(RM) git-credential-osxkeychain git-credential-osxkeychain.o\ndiff --git a/contrib/credential-osxkeychain/git-credential-osxkeychain.c b/contrib/credential-osxkeychain/git-credential-osxkeychain.c\nnew file mode 100644\nindex 0000000000..2f611f7348\n--- /dev/null\n+++ b/contrib/credential-osxkeychain/git-credential-osxkeychain.c\n@@ -0,0 +1,300 @@\n+/* Copyright 2011 Jay Soffian. All rights reserved.\n+ * FreeBSD License.\n+ *\n+ * A git credential helper that interfaces with the Mac OS X keychain\n+ * via the Security framework.\n+ */\n+#include <fcntl.h>\n+#include <stdio.h>\n+#include <string.h>\n+#include <stdlib.h>\n+#include <termios.h>\n+#include <Security/Security.h>\n+\n+static void die(const char *err, ...)\n+{\n+\tchar msg[4096];\n+\tva_list params;\n+\tva_start(params, err);\n+\tvsnprintf(msg, sizeof(msg), err, params);\n+\tfprintf(stderr, \"%s\\n\", msg);\n+\tva_end(params);\n+\texit(1);\n+}\n+\n+void *xmalloc(size_t size)\n+{\n+\tvoid *ret = malloc(size);\n+\tif (!ret)\n+\t\tdie(\"Out of memory\");\n+\treturn ret;\n+}\n+\n+void *xstrdup(const char *s1)\n+{\n+\tvoid *ret = strdup(s1);\n+\tif (!ret)\n+\t\tdie(\"Out of memory\");\n+\treturn ret;\n+}\n+\n+void emit_user_pass(char *username, char *password)\n+{\n+\tif (username)\n+\t\tprintf(\"username=%s\\n\", username);\n+\tif (password)\n+\t\tprintf(\"password=%s\\n\", password);\n+}\n+\n+typedef enum { USERNAME, PASSWORD } prompt_type;\n+\n+void prompt(FILE *file, const char *what, const char *desc)\n+{\n+\tif (desc)\n+\t\tfprintf(file, \"%s for '%s': \", what, desc);\n+\telse\n+\t\tfprintf(file, \"%s: \", what);\n+}\n+\n+char *prompt_tty(prompt_type what, char *description)\n+{\n+\tstruct termios old;\n+\tstruct termios new;\n+\tchar buf[128];\n+\tint buf_len;\n+\tint fd = open(\"/dev/tty\", O_RDWR|O_NOCTTY);\n+\tFILE *tty = fdopen(fd, \"w+\");\n+\tif (what == USERNAME) {\n+\t\tprompt(tty, \"Username\", description);\n+\t}\n+\telse {\n+\t\tprompt(tty, \"Password\", description);\n+\t\ttcgetattr(fd, &old);\n+\t\tmemcpy(&new, &old, sizeof(struct termios));\n+\t\tnew.c_lflag &= ~ECHO;\n+\t\ttcsetattr(fd, TCSADRAIN, &new);\n+\t}\n+\tif (!fgets(buf, sizeof(buf), tty)) {\n+\t\tfprintf(tty, \"\\n\");\n+\t\tfclose(tty);\n+\t\treturn NULL;\n+\t}\n+\tif (what == PASSWORD) {\n+\t\ttcsetattr(fd, TCSADRAIN, &old);\n+\t\tfprintf(tty, \"\\n\");\n+\t}\n+\tfclose(tty);\n+\tbuf_len = strlen(buf);\n+\tif (buf[buf_len-1] == '\\n')\n+\t\tbuf[buf_len-1] = '\\0';\n+\treturn xstrdup(buf);\n+}\n+\n+char *username_from_keychain_item(SecKeychainItemRef item)\n+{\n+\tOSStatus status;\n+\tSecKeychainAttributeList list;\n+\tSecKeychainAttribute attr;\n+\tlist.count = 1;\n+\tlist.attr = &attr;\n+\tattr.tag = kSecAccountItemAttr;\n+\tchar *username;\n+\n+\tstatus = SecKeychainItemCopyContent(item, NULL, &list, NULL, NULL);\n+\tif (status != noErr)\n+\t\treturn NULL;\n+\tusername = xmalloc(attr.length + 1);\n+\tstrncpy(username, attr.data, attr.length);\n+\tusername[attr.length] = '\\0';\n+\tSecKeychainItemFreeContent(&list, NULL);\n+\treturn username;\n+}\n+\n+int find_internet_password(SecProtocolType protocol,\n+\t\t\t   char *hostname,\n+\t\t\t   char *username)\n+{\n+\tvoid *password_buf;\n+\tUInt32 password_len;\n+\tOSStatus status;\n+\tchar *password;\n+\tint free_username = 0;\n+\tSecKeychainItemRef item;\n+\n+\tstatus = SecKeychainFindInternetPassword(\n+\t\t\tNULL,\n+\t\t\tstrlen(hostname), hostname,\n+\t\t\t0, NULL,\n+\t\t\tusername ? strlen(username) : 0, username,\n+\t\t\t0, NULL,\n+\t\t\t0,\n+\t\t\tprotocol,\n+\t\t\tkSecAuthenticationTypeDefault,\n+\t\t\t&password_len, &password_buf,\n+\t\t\t&item);\n+\tif (status != noErr)\n+\t\treturn -1;\n+\n+\tpassword = xmalloc(password_len + 1);\n+\tstrncpy(password, password_buf, password_len);\n+\tpassword[password_len] = '\\0';\n+\tSecKeychainItemFreeContent(NULL, password_buf);\n+\tif (!username) {\n+\t\tusername = username_from_keychain_item(item);\n+\t\tfree_username = 1;\n+\t}\n+\temit_user_pass(username, password);\n+\tif (free_username)\n+\t\tfree(username);\n+\tfree(password);\n+\treturn 0;\n+}\n+\n+void delete_internet_password(SecProtocolType protocol,\n+\t\t\t      char *hostname,\n+\t\t\t      char *username)\n+{\n+\tOSStatus status;\n+\tSecKeychainItemRef item;\n+\n+\tstatus = SecKeychainFindInternetPassword(\n+\t\t\tNULL,\n+\t\t\tstrlen(hostname), hostname,\n+\t\t\t0, NULL,\n+\t\t\tusername ? strlen(username) : 0, username,\n+\t\t\t0, NULL,\n+\t\t\t0,\n+\t\t\tprotocol,\n+\t\t\tkSecAuthenticationTypeDefault,\n+\t\t\t0, NULL,\n+\t\t\t&item);\n+\tif (status != noErr)\n+\t\treturn;\n+\tSecKeychainItemDelete(item);\n+}\n+\n+void add_internet_password(SecProtocolType protocol,\n+\t\t\t   char *hostname,\n+\t\t\t   char *username,\n+\t\t\t   char *password,\n+\t\t\t   char *comment)\n+{\n+\tconst char *label_format = \"%s (%s)\";\n+\tchar *label;\n+\tOSStatus status;\n+\tSecKeychainItemRef item;\n+\tSecKeychainAttributeList list;\n+\tSecKeychainAttribute attr;\n+\tlist.count = 1;\n+\tlist.attr = &attr;\n+\tstatus = SecKeychainAddInternetPassword(\n+\t\t\tNULL,\n+\t\t\tstrlen(hostname), hostname,\n+\t\t\t0, NULL,\n+\t\t\tstrlen(username), username,\n+\t\t\t0, NULL,\n+\t\t\t0,\n+\t\t\tprotocol,\n+\t\t\tkSecAuthenticationTypeDefault,\n+\t\t\tstrlen(password), password,\n+\t\t\t&item);\n+\tif (status != noErr)\n+\t\treturn;\n+\n+\t/* set the comment */\n+\tattr.tag = kSecCommentItemAttr;\n+\tattr.data = comment;\n+\tattr.length = strlen(comment);\n+\tSecKeychainItemModifyContent(item, &list, 0, NULL);\n+\n+\t/* override the label */\n+\tlabel = xmalloc(strlen(hostname) + strlen(username) +\n+\t\t\tstrlen(label_format));\n+\tsprintf(label, label_format, hostname, username);\n+\tattr.tag = kSecLabelItemAttr;\n+\tattr.data = label;\n+\tattr.length = strlen(label);\n+\tSecKeychainItemModifyContent(item, &list, 0, NULL);\n+}\n+\n+int main(int argc, const char **argv)\n+{\n+\tconst char *usage =\n+\t\t\"Usage: git credential-osxkeychain --unique=TOKEN [options]\\n\"\n+\t\t\"Options:\\n\"\n+\t\t\"    --description=DESCRIPTION\\n\"\n+\t\t\"    --username=USERNAME\\n\"\n+\t\t\"    --reject\";\n+\tchar *description = NULL, *username = NULL, *unique = NULL;\n+\tchar *hostname, *password;\n+\tint i, free_username = 0, reject = 0;\n+\tSecProtocolType protocol = 0;\n+\n+\tfor (i = 1; i < argc; i++) {\n+\t\tconst char *arg = argv[i];\n+\t\tif (!strncmp(arg, \"--description=\", 14)) {\n+\t\t\tdescription = (char *) arg + 14;\n+\t\t}\n+\t\telse if (!strncmp(arg, \"--username=\", 11)) {\n+\t\t\tusername = (char *) arg + 11;\n+\t\t}\n+\t\telse if (!strncmp(arg, \"--unique=\", 9)) {\n+\t\t\tunique = (char *) arg + 9;\n+\t\t}\n+\t\telse if (!strcmp(arg, \"--reject\")) {\n+\t\t\treject = 1;\n+\t\t}\n+\t\telse if (!strcmp(arg, \"--help\")) {\n+\t\t\tdie(usage);\n+\t\t}\n+\t\telse\n+\t\t\tdie(\"Unrecognized argument `%s'; try --help\", arg);\n+\t}\n+\n+\tif (!unique)\n+\t\tdie(\"Must specify --unique=TOKEN; try --help\");\n+\n+\thostname = strchr(unique, ':');\n+\tif (!hostname)\n+\t\tdie(\"Invalid token `%s'\", unique);\n+\t*hostname++ = '\\0';\n+\n+\t/* \"GitHub for Mac\" compatibility */\n+\tif (!strcmp(hostname, \"github.com\"))\n+\t\thostname = \"github.com/mac\";\n+\n+\tif (!strcmp(unique, \"https\")) {\n+\t\tprotocol = kSecProtocolTypeHTTPS;\n+\t} else if (!strcmp(unique, \"http\")) {\n+\t\tprotocol = kSecProtocolTypeHTTP;\n+\t}\n+\telse\n+\t\tdie(\"Unrecognized protocol `%s'\", unique);\n+\n+\t/* if this is a rejection delete the existing creds */\n+\tif (reject) {\n+\t\tdelete_internet_password(protocol, hostname, username);\n+\t\treturn 0;\n+\t}\n+\n+\t/* otherwise look for a matching keychain item */\n+\tif (!find_internet_password(protocol, hostname, username))\n+\t\treturn 0;\n+\n+\t/* no keychain item found, prompt the user and store the result */\n+\tif (!username) {\n+\t\tif (!(username = prompt_tty(USERNAME, description)))\n+\t\t\treturn 0;\n+\t\tfree_username = 1;\n+\t}\n+\tif (!(password = prompt_tty(PASSWORD, description)))\n+\t\treturn 0;\n+\n+\tadd_internet_password(protocol, hostname, username, password,\n+\t\t\t      description ? description : \"default\");\n+\temit_user_pass(username, password);\n+\tif (free_username)\n+\t\tfree(username);\n+\tfree(password);\n+\treturn 0;\n+}\ndiff --git a/contrib/credential-osxkeychain/git-credential-osxkeychain.py b/contrib/credential-osxkeychain/git-credential-osxkeychain.py\nnew file mode 100755\nindex 0000000000..ae5ec00d68\n--- /dev/null\n+++ b/contrib/credential-osxkeychain/git-credential-osxkeychain.py\n@@ -0,0 +1,148 @@\n+#!/usr/bin/python\n+# Copyright 2011 Jay Soffian. All rights reserved.\n+# FreeBSD License.\n+\"\"\"\n+A git credential helper that interfaces with the Mac OS X keychain via\n+/usr/bin/security.\n+\"\"\"\n+\n+import os\n+import re\n+import sys\n+import termios\n+from getpass import _raw_input\n+from optparse import OptionParser\n+from subprocess import Popen, PIPE\n+\n+USERNAME = 'USERNAME'\n+PASSWORD = 'PASSWORD'\n+PROMPTS = dict(USERNAME='Username', PASSWORD='Password')\n+\n+def prompt_tty(what, desc):\n+    \"\"\"Prompt on TTY for username or password with optional description\"\"\"\n+    prompt = '%s%s: ' % (PROMPTS[what], \" for '%s'\" % desc if desc else '')\n+    # Borrowed mostly from getpass.py\n+    fd = os.open('/dev/tty', os.O_RDWR|os.O_NOCTTY)\n+    tty = os.fdopen(fd, 'w+', 1)\n+    if what == USERNAME:\n+        return _raw_input(prompt, tty, tty)\n+    old = termios.tcgetattr(fd) # a copy to save\n+    new = old[:]\n+    new[3] &= ~termios.ECHO  # 3 == 'lflags'\n+    try:\n+        termios.tcsetattr(fd, termios.TCSADRAIN, new)\n+        return _raw_input(prompt, tty, tty)\n+    finally:\n+        termios.tcsetattr(fd, termios.TCSADRAIN, old)\n+        tty.write('\\n')\n+\n+def emit_user_pass(username, password):\n+    if username:\n+        print 'username=' + username\n+    if password:\n+        print 'password=' + password\n+\n+def make_security_args(command, protocol, hostname, username):\n+    args = ['/usr/bin/security', command]\n+    # tlfd is 'dflt' backwards - obvious /usr/bin/security bug\n+    # but allows us to ignore matching saved web forms.\n+    args.extend(['-t', 'tlfd'])\n+    args.extend(['-r', protocol])\n+    if hostname:\n+        args.extend(['-s', hostname])\n+    if username:\n+        args.extend(['-a', username])\n+    return args\n+\n+def find_internet_password(protocol, hostname, username):\n+    args = make_security_args('find-internet-password',\n+                              protocol, hostname, username)\n+    args.append('-g') # asks for password on stderr\n+    p = Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE)\n+    # grok stdout for username\n+    out, err = p.communicate()\n+    if p.returncode != 0:\n+        return\n+    for line in out.splitlines(): # pylint:disable-msg=E1103\n+        m = re.search(r'^\\s+\"acct\"<blob>=[^\"]*\"(.*)\"$', line)\n+        if m:\n+            username = m.group(1)\n+            break\n+    # grok stderr for password\n+    m = re.search(r'^password:[^\"]*\"(.*)\"$', err)\n+    if not m:\n+        return\n+    emit_user_pass(username, m.group(1))\n+    return True\n+\n+def delete_internet_password(protocol, hostname, username):\n+    args = make_security_args('delete-internet-password',\n+                              protocol, hostname, username)\n+    p = Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE)\n+    p.communicate()\n+\n+def add_internet_password(protocol, hostname, username, password):\n+    # We do this over a pipe so that we can provide the password more\n+    # securely than as an argument which would show up in ps output.\n+    # Unfortunately this is possibly less robust since the security man\n+    # page does not document how to quote arguments. Emprically it seems\n+    # that using the double-quote, escaping \\ and \" works properly.\n+    username = username.replace('\\\\', '\\\\\\\\').replace('\"', '\\\\\"')\n+    password = password.replace('\\\\', '\\\\\\\\').replace('\"', '\\\\\"')\n+    command = ' '.join([\n+        'add-internet-password', '-U',\n+        '-r', protocol,\n+        '-s', hostname,\n+        '-a \"%s\"' % username,\n+        '-w \"%s\"' % password,\n+        '-j default',\n+        '-l \"%s (%s)\"' % (hostname, username),\n+    ]) + '\\n'\n+    args = ['/usr/bin/security', '-i']\n+    p = Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE)\n+    p.communicate(command)\n+\n+def main():\n+    p = OptionParser()\n+    p.add_option('--description')\n+    p.add_option('--reject', action='store_true')\n+    p.add_option('--unique', dest='token', help='REQUIRED OPTION')\n+    p.add_option('--username')\n+    opts, _ = p.parse_args()\n+\n+    if not opts.token:\n+        p.error('--unique option required')\n+    if not ':' in opts.token:\n+        print >> sys.stderr, \"Invalid token: '%s'\" % opts.token\n+        return 1\n+    protocol, hostname = opts.token.split(':', 1)\n+    if protocol not in ('http', 'https'):\n+        print >> sys.stderr, \"Unsupported protocol: '%s'\" % protocol\n+        return 1\n+    if protocol == 'https':\n+        protocol = 'htps'\n+\n+    # \"GitHub for Mac\" compatibility\n+    if hostname == 'github.com':\n+        hostname = 'github.com/mac'\n+\n+    # if this is a rejection delete the existing creds\n+    if opts.reject:\n+        delete_internet_password(protocol, hostname, opts.username)\n+        return 0\n+\n+    # otherwise look for creds\n+    if find_internet_password(protocol, hostname, opts.username):\n+        return 0\n+\n+    # creds not found, so prompt the user then store the creds\n+    username = opts.username\n+    if username is None:\n+        username = prompt_tty(USERNAME, opts.description)\n+    password = prompt_tty(PASSWORD, opts.description)\n+    add_internet_password(protocol, hostname, username, password)\n+    emit_user_pass(username, password)\n+    return 0\n+\n+if __name__ == '__main__':\n+    sys.exit(main())\n-- \n1.7.7.rc1.1.g011e1\n"},{"id":"176476","messageId":"20110929075627.GB14022@sigill.intra.peff.net","threadId":"28393","inReplyTo":"1316055113-2353-1-git-send-email-jaysoffian@gmail.com","subject":"Re: [PATCH] contrib: add a pair of credential helpers for Mac OS X's keychain","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2011-09-29T07:56:27Z","receivedAt":"2011-09-29T07:56:27Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Sep 14, 2011 at 10:51:53PM -0400, Jay Soffian wrote:\n\n> This credential helper adds, searches, and removes entries from\n> the Mac OS X keychain. The C version links against the Security\n> framework and is probably the best choice for daily use.\n> \n> A python version is also included primarily as a more readable\n> example and uses the /usr/bin/security CLI to access the keychain.\n> \n> Tested with 10.6.8.\n\nSo I finally got a nice working OS X setup (10.7) to play around with\nthese. Overall, works as advertised. :) I have a few comments, though.\n\n> Here's a C version that no longer links to git. I also kept the original\n> Python version as an example. I decided not to call out to\n> 'git credential-gitpass' as it was simple enough to manage /dev/tty\n> and there's no portability issues since this is OS X specific.\n\nThis was my first one. I kind of expected there to be some kind of\ngraphical password dialog. Especially because keychain will pop up a\ndialog and ask you \"is it OK for git to access this password?\". So I\nsort of assumed that people would assume that credentials happened\noutside of the regular terminal session (I see the same thing on Linux,\nfor example, with gpg-agent, which will open a new window and grab\nfocus).\n\nBut I have no idea what's \"normal\" on OS X.\n\nI wondered if you were trying to be friendly to people who were\nconnecting via ssh. But that doesn't seem to work at all. I couldn't get\neither version of your helper to actually do anything in an ssh session\n(even with the same user logged in on console).  I guess there is some\nmagic to hook it into the keychain manager.\n\nAlso, regarding opening /dev/tty yourself versus using getpass. There\nare a few magic things getpass will do that your helper won't:\n\n  1. It respects core.askpass, GIT_ASKPASS, and SSH_ASKPASS if they are\n     set.\n\n  2. The \"get the username from the config\" feature is triggered at the\n     time of prompting the user (so instead of asking for the username,\n     we check the config and pretend the user told us).\n\n     I did it this way originally so that helpers would have the first\n     crack at setting a username, and we would fall back to the config.\n     Thinking on it more, that may be backwards. If the user has told\n     git \"for github.com, I am user 'foo'\", then that should probably\n     take effect first, and --username=foo get passed to the helper.\n\n     It doesn't make a big difference with long-term storage helpers,\n     because you tell them your username once and they remember it. But\n     for things like credential-cache, it lets you store the username\n     for a long time, but only cache the password (which means not\n     typing the username every time).\n\nSo I think maybe reason (2) should go away. But (1) is definitely worth\nconsidering.\n\n> +       if (!unique)\n> +               die(\"Must specify --unique=TOKEN; try --help\");\n\nMy test harness checks that this case just asks for the password without\nbothering to do any lookup or storage. It probably doesn't really matter\nin practice; I think git should always be providing _some_ context.\n\n> +\thostname = strchr(unique, ':');\n> +\tif (!hostname)\n> +\t\tdie(\"Invalid token `%s'\", unique);\n> +\t*hostname++ = '\\0';\n\nHrm. I was really hoping people wouldn't need to pick apart the \"unique\"\ntoken, and it could remain an opaque blob. If helpers are going to do\nthis sort of parsing, then I'd just as soon have git break it down for\nthem, and do something like:\n\n  git credential-osxkeychain \\\n    --protocol=https \\\n    --host=github.com \\\n    --path=peff/git.git\n    --username=peff\n\nto just hand over as much information as possible, and let the helper\nthrow it all together if it wants to.\n\n> +\t/* \"GitHub for Mac\" compatibility */\n> +\tif (!strcmp(hostname, \"github.com\"))\n> +\t\thostname = \"github.com/mac\";\n\nNice touch. :)\n\n> +\tif (!strcmp(unique, \"https\")) {\n> +\t\tprotocol = kSecProtocolTypeHTTPS;\n> +\t} else if (!strcmp(unique, \"http\")) {\n> +\t\tprotocol = kSecProtocolTypeHTTP;\n> +\t}\n> +\telse\n> +\t\tdie(\"Unrecognized protocol `%s'\", unique);\n\nMy series will also produce \"cert:/path/to/certificate\" when unlocking a\ncertificate. The other candidates for conversion are smtp-auth (for\nsend-email) and imap (for imap-send).  I guess for certs, you'd want to\nuse the \"generic\" keychain type.\n\nI wonder if some people would not want to cache cert passwords. Speaking\nof which, I remember keychain asking me \"do you want to let git see this\npassword?\", but I don't ever remember it asking \"do you want to save\nthis password?\". Is that usually automatic? Again, I was kind of\nexpecting a dialog with a \"remember this\" checkbox.\n\n> +def add_internet_password(protocol, hostname, username, password):\n> +    # We do this over a pipe so that we can provide the password more\n> +    # securely than as an argument which would show up in ps output.\n> +    # Unfortunately this is possibly less robust since the security man\n> +    # page does not document how to quote arguments. Emprically it seems\n> +    # that using the double-quote, escaping \\ and \" works properly.\n> +    username = username.replace('\\\\', '\\\\\\\\').replace('\"', '\\\\\"')\n> +    password = password.replace('\\\\', '\\\\\\\\').replace('\"', '\\\\\"')\n> +    command = ' '.join([\n> +        'add-internet-password', '-U',\n> +        '-r', protocol,\n> +        '-s', hostname,\n> +        '-a \"%s\"' % username,\n> +        '-w \"%s\"' % password,\n> +        '-j default',\n> +        '-l \"%s (%s)\"' % (hostname, username),\n> +    ]) + '\\n'\n> +    args = ['/usr/bin/security', '-i']\n> +    p = Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE)\n> +    p.communicate(command)\n\nI noticed that when using the python helper, the dialog asking something\nlike: \"security wants to know this password. Allow it?\"\n\nWhich was kind of lame. I would hope we could convince it to say \"git\".\nBut I didn't see any option in the \"security\" tool for specifying the\ncontext[1]. The C helper says \"git-credential-osxkeychain\". Which isn't\nthe end of the world, but it would be prettier if it just said \"git\".\n\n-Peff\n\n[1] I can kind of see why they might not want you to set this for\nsecurity reasons (because it makes impersonating other programs easy).\nOn the other hand, saying \"security\" conveys absolutely nothing. And as\nfar as I can tell, I could just call my program /tmp/iTunes, and it\nwould say \"iTunes wants to know this password...\".\n"},{"id":"176477","messageId":"53869134-BC37-4B71-AB90-E7F1AABC633B@gmail.com","threadId":"28393","inReplyTo":"20110929075627.GB14022@sigill.intra.peff.net","subject":"Re: [PATCH] contrib: add a pair of credential helpers for Mac OS X's keychain","fromName":"Chris Mear","fromEmail":"chrismear@gmail.com","sentAt":"2011-09-29T08:19:42Z","receivedAt":"2011-09-29T08:19:42Z","isPatch":true,"sender":{"key":"chrismear@gmail.com","avatar":null},"body":"On 29 Sep 2011, at 08:56, Jeff King <peff@peff.net> wrote:\n\n>> Here's a C version that no longer links to git. I also kept the original\n>> Python version as an example. I decided not to call out to\n>> 'git credential-gitpass' as it was simple enough to manage /dev/tty\n>> and there's no portability issues since this is OS X specific.\n> \n> This was my first one. I kind of expected there to be some kind of\n> graphical password dialog. Especially because keychain will pop up a\n> dialog and ask you \"is it OK for git to access this password?\". So I\n> sort of assumed that people would assume that credentials happened\n> outside of the regular terminal session (I see the same thing on Linux,\n> for example, with gpg-agent, which will open a new window and grab\n> focus).\n> \n> But I have no idea what's \"normal\" on OS X.\n\nIt's normal for applications on OS X to present their own UI when asking for credentials in order to save them to the keychain. For example, web passwords stored by Safari are captured via the normal web page form elements, but stored in the keychain.\n\nThat said, most terminal applications aren't aware of the Mac OS X keychain. So I suppose it may surprise users to come across the password prompt in the terminal UI, even though that is the same pattern that graphical programs follow.\n\nBut it's not without precedent: I'm pretty certain Subversion collects credentials directly in the terminal for storage in the keychain.\n\nChris\n"},{"id":"176479","messageId":"CAEBDL5WhpVg17aPuRqrE5=2Q293kVD4fYtxGqRzx_K=87t-jgw@mail.gmail.com","threadId":"28393","inReplyTo":"20110929075627.GB14022@sigill.intra.peff.net","subject":"Re: [PATCH] contrib: add a pair of credential helpers for Mac OS X's keychain","fromName":"John Szakmeister","fromEmail":"john@szakmeister.net","sentAt":"2011-09-29T10:03:29Z","receivedAt":"2011-09-29T10:03:29Z","isPatch":true,"sender":{"key":"john@szakmeister.net","avatar":"https://avatars.githubusercontent.com/u/448087?v=4"},"body":"On Thu, Sep 29, 2011 at 3:56 AM, Jeff King <peff@peff.net> wrote:\n[snip]\n> This was my first one. I kind of expected there to be some kind of\n> graphical password dialog. Especially because keychain will pop up a\n> dialog and ask you \"is it OK for git to access this password?\". So I\n> sort of assumed that people would assume that credentials happened\n> outside of the regular terminal session (I see the same thing on Linux,\n> for example, with gpg-agent, which will open a new window and grab\n> focus).\n>\n> But I have no idea what's \"normal\" on OS X.\n\nI've been working on a version of the keychain credential cache as\nwell.  I did create a gui, although it's a bit painful.\n\n> I wondered if you were trying to be friendly to people who were\n> connecting via ssh. But that doesn't seem to work at all. I couldn't get\n> either version of your helper to actually do anything in an ssh session\n> (even with the same user logged in on console).  I guess there is some\n> magic to hook it into the keychain manager.\n\nI'm not sure there is.  For instance, ssh will look up passphrases for\nssh keys in the keychain, but if you ssh into the box, and then try to\nssh to somewhere else, it'll prompt you for the passphrase for the\nkey.  In my own experiments, the lookup will come back with a failure,\ndespite the fact that the keychain is already unlocked. :-(\n\nLooking at the Subversion source, they recognize the issue too:\n\n> * XXX (2005-12-07): If no GUI is available (e.g. over a SSH session),\n> * you won't be prompted for credentials with which to unlock your\n> * keychain.  Apple recognizes lack of TTY prompting as a known\n> * problem.\n\n...obviously, Apple hasn't fixed this issue. :-(\n\n> Also, regarding opening /dev/tty yourself versus using getpass. There\n> are a few magic things getpass will do that your helper won't:\n>\n>  1. It respects core.askpass, GIT_ASKPASS, and SSH_ASKPASS if they are\n>     set.\n>\n>  2. The \"get the username from the config\" feature is triggered at the\n>     time of prompting the user (so instead of asking for the username,\n>     we check the config and pretend the user told us).\n>\n>     I did it this way originally so that helpers would have the first\n>     crack at setting a username, and we would fall back to the config.\n>     Thinking on it more, that may be backwards. If the user has told\n>     git \"for github.com, I am user 'foo'\", then that should probably\n>     take effect first, and --username=foo get passed to the helper.\n\nI think that makes sense.  I think one thing we have to be careful\nabout partial matches.  I wouldn't want the credential cache to send\noff the wrong password to a service.  This may be me being cautious,\nbut if I don't have all the necessary bits, I'd rather we fail that to\nguess which entry is right.\n\n[snip]\n> Hrm. I was really hoping people wouldn't need to pick apart the \"unique\"\n> token, and it could remain an opaque blob. If helpers are going to do\n> this sort of parsing, then I'd just as soon have git break it down for\n> them, and do something like:\n>\n>  git credential-osxkeychain \\\n>    --protocol=https \\\n>    --host=github.com \\\n>    --path=peff/git.git\n>    --username=peff\n>\n> to just hand over as much information as possible, and let the helper\n> throw it all together if it wants to.\n\nI think this is a good idea too.  Keychain definitely wants this\ninformation stored in separate fields, and I suspect the secrets api\ndoes too (I haven't found any formal docs about the preferred way of\nstoring attributes related to urls though).\n\n>> +     /* \"GitHub for Mac\" compatibility */\n>> +     if (!strcmp(hostname, \"github.com\"))\n>> +             hostname = \"github.com/mac\";\n>\n> Nice touch. :)\n\nI honestly don't understand why this needs to be done.  I don't use\nGitHub for Mac... does that mean this is busted for me?\n\n[snip]\n> My series will also produce \"cert:/path/to/certificate\" when unlocking a\n> certificate. The other candidates for conversion are smtp-auth (for\n> send-email) and imap (for imap-send).  I guess for certs, you'd want to\n> use the \"generic\" keychain type.\n\nThere is a method for adding a certificate to the keychain:\n   <http://developer.apple.com/library/mac/#documentation/Security/Reference/certifkeytrustservices/Reference/reference.html#//apple_ref/doc/uid/TP30000157>\n\nI'm not sure what that does exactly, but I do have a cert, and it\nshows up as \"certificate\" in the keychain.\n\n> I wonder if some people would not want to cache cert passwords. Speaking\n> of which, I remember keychain asking me \"do you want to let git see this\n> password?\", but I don't ever remember it asking \"do you want to save\n> this password?\". Is that usually automatic? Again, I was kind of\n> expecting a dialog with a \"remember this\" checkbox.\n\nBy the time you get Keychain involved, the decision has been made.\nMost applications offer that ability... and you're right, this should\nprobably offer the same capability.  That also means stashing that\ndata somewhere. :-(  OTOH, it does make for a better user experience.\n\n[snip]\n> I noticed that when using the python helper, the dialog asking something\n> like: \"security wants to know this password. Allow it?\"\n>\n> Which was kind of lame. I would hope we could convince it to say \"git\".\n> But I didn't see any option in the \"security\" tool for specifying the\n> context[1]. The C helper says \"git-credential-osxkeychain\". Which isn't\n> the end of the world, but it would be prettier if it just said \"git\".\n\nI'm not sure how easy it is to do that.  I think it's meant to be a\nsecurity measure that it points out the executable name... but it does\nmake it less friendly for users. :-(\n\n> -Peff\n>\n> [1] I can kind of see why they might not want you to set this for\n> security reasons (because it makes impersonating other programs easy).\n> On the other hand, saying \"security\" conveys absolutely nothing. And as\n> far as I can tell, I could just call my program /tmp/iTunes, and it\n> would say \"iTunes wants to know this password...\".\n\nYep, I agree.  And it's worse when using the security command line\ntool... when you grant security access to the key, then any app could\ntechnically gain access to the item via the security tool.  That's one\nof the reasons I didn't pursue that route early on.\n\n-John\n"},{"id":"176548","messageId":"CAEBDL5VbaafKYjJc6spgz94Z4R7QprA7vFPMGWhgk5QY99-wBQ@mail.gmail.com","threadId":"28393","inReplyTo":"CAEBDL5WhpVg17aPuRqrE5=2Q293kVD4fYtxGqRzx_K=87t-jgw@mail.gmail.com","subject":"Re: [PATCH] contrib: add a pair of credential helpers for Mac OS X's keychain","fromName":"John Szakmeister","fromEmail":"john@szakmeister.net","sentAt":"2011-09-30T01:17:11Z","receivedAt":"2011-09-30T01:17:11Z","isPatch":true,"sender":{"key":"john@szakmeister.net","avatar":"https://avatars.githubusercontent.com/u/448087?v=4"},"body":"On Thu, Sep 29, 2011 at 6:03 AM, John Szakmeister <john@szakmeister.net> wrote:\n[snip]\n> Yep, I agree.  And it's worse when using the security command line\n> tool... when you grant security access to the key, then any app could\n> technically gain access to the item via the security tool.  That's one\n> of the reasons I didn't pursue that route early on.\n\nThinking about this a little more, git-credential-anything has the\nsame problem.  I can run it, and it'll dump out my password for\nanybody.  I'd rather it didn't do that.  I think it would be more\nsatisfying to have the mechanisms built into git itself, without a\nseparate application.  I'm not sure how practical that is though.\n\n-John\n"},{"id":"176590","messageId":"CAG+J_DwntGc+j3duCVqsnoJGV18FqnwXJ99C1XqKope_zbGHAA@mail.gmail.com","threadId":"28393","inReplyTo":"20110929075627.GB14022@sigill.intra.peff.net","subject":"Re: [PATCH] contrib: add a pair of credential helpers for Mac OS X's keychain","fromName":"Jay Soffian","fromEmail":"jaysoffian@gmail.com","sentAt":"2011-09-30T19:16:58Z","receivedAt":"2011-09-30T19:16:58Z","isPatch":true,"sender":{"key":"jaysoffian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/155970?v=4"},"body":"On Thu, Sep 29, 2011 at 3:56 AM, Jeff King <peff@peff.net> wrote:\n> On Wed, Sep 14, 2011 at 10:51:53PM -0400, Jay Soffian wrote:\n>\n>> This credential helper adds, searches, and removes entries from\n>> the Mac OS X keychain. The C version links against the Security\n>> framework and is probably the best choice for daily use.\n>>\n>> A python version is also included primarily as a more readable\n>> example and uses the /usr/bin/security CLI to access the keychain.\n>>\n>> Tested with 10.6.8.\n>\n> So I finally got a nice working OS X setup (10.7) to play around with\n> these. Overall, works as advertised. :) I have a few comments, though.\n>\n>> Here's a C version that no longer links to git. I also kept the original\n>> Python version as an example. I decided not to call out to\n>> 'git credential-gitpass' as it was simple enough to manage /dev/tty\n>> and there's no portability issues since this is OS X specific.\n>\n> This was my first one. I kind of expected there to be some kind of\n> graphical password dialog. Especially because keychain will pop up a\n> dialog and ask you \"is it OK for git to access this password?\". So I\n> sort of assumed that people would assume that credentials happened\n> outside of the regular terminal session (I see the same thing on Linux,\n> for example, with gpg-agent, which will open a new window and grab\n> focus).\n>\n> But I have no idea what's \"normal\" on OS X.\n\nThis makes no sense to me at all. Ignore OS X for the moment. You use\ngit on the command-line. Why would there be any expectation of it\ninteracting with the user via anything other than the terminal.\n\nAnyway, I expect the username/password prompt on the command-line, in\nthe same terminal window where I just ran the git command that needs\ncredentials.\n\n> I wondered if you were trying to be friendly to people who were\n> connecting via ssh. But that doesn't seem to work at all. I couldn't get\n> either version of your helper to actually do anything in an ssh session\n> (even with the same user logged in on console).  I guess there is some\n> magic to hook it into the keychain manager.\n\nI don't understand where you're running ssh from/to in this scenario,\nbut OS X has a notion of security contexts (this is a bit of a\ntangent):\n\nhttp://developer.apple.com/library/mac/#technotes/tn2083/_index.html\n\n> Also, regarding opening /dev/tty yourself versus using getpass. There\n> are a few magic things getpass will do that your helper won't:\n>\n>  1. It respects core.askpass, GIT_ASKPASS, and SSH_ASKPASS if they are\n>     set.\n>\n>  2. The \"get the username from the config\" feature is triggered at the\n>     time of prompting the user (so instead of asking for the username,\n>     we check the config and pretend the user told us).\n>\n>     I did it this way originally so that helpers would have the first\n>     crack at setting a username, and we would fall back to the config.\n>     Thinking on it more, that may be backwards. If the user has told\n>     git \"for github.com, I am user 'foo'\", then that should probably\n>     take effect first, and --username=foo get passed to the helper.\n>\n>     It doesn't make a big difference with long-term storage helpers,\n>     because you tell them your username once and they remember it. But\n>     for things like credential-cache, it lets you store the username\n>     for a long time, but only cache the password (which means not\n>     typing the username every time).\n>\n> So I think maybe reason (2) should go away. But (1) is definitely worth\n> considering.\n\nI found it ugly that git's native getpass doesn't echo the username\nback, and it seems hackish to me for the credential helper to turn\nback around and invoke it in any case. :-(\n\n>> +       if (!unique)\n>> +               die(\"Must specify --unique=TOKEN; try --help\");\n>\n> My test harness checks that this case just asks for the password without\n> bothering to do any lookup or storage. It probably doesn't really matter\n> in practice; I think git should always be providing _some_ context.\n\nOkay, that wasn't clear from whatever documentation I read on how\ncredential helpers should behave. But why invoke the credential helper\njust to ask for a password?\n\n>> +     hostname = strchr(unique, ':');\n>> +     if (!hostname)\n>> +             die(\"Invalid token `%s'\", unique);\n>> +     *hostname++ = '\\0';\n>\n> Hrm. I was really hoping people wouldn't need to pick apart the \"unique\"\n> token, and it could remain an opaque blob. If helpers are going to do\n> this sort of parsing, then I'd just as soon have git break it down for\n> them, and do something like:\n>\n>  git credential-osxkeychain \\\n>    --protocol=https \\\n>    --host=github.com \\\n>    --path=peff/git.git\n>    --username=peff\n>\n> to just hand over as much information as possible, and let the helper\n> throw it all together if it wants to.\n\nKeychain entries have distinct fields. I broke apart the token and\nstored it the way other applications mostly do on OS X.\n\n>> +     /* \"GitHub for Mac\" compatibility */\n>> +     if (!strcmp(hostname, \"github.com\"))\n>> +             hostname = \"github.com/mac\";\n>\n> Nice touch. :)\n>\n>> +     if (!strcmp(unique, \"https\")) {\n>> +             protocol = kSecProtocolTypeHTTPS;\n>> +     } else if (!strcmp(unique, \"http\")) {\n>> +             protocol = kSecProtocolTypeHTTP;\n>> +     }\n>> +     else\n>> +             die(\"Unrecognized protocol `%s'\", unique);\n>\n> My series will also produce \"cert:/path/to/certificate\" when unlocking a\n> certificate. The other candidates for conversion are smtp-auth (for\n> send-email) and imap (for imap-send).  I guess for certs, you'd want to\n> use the \"generic\" keychain type.\n\nYep, I was punting on certificate for v1.\n\n> I wonder if some people would not want to cache cert passwords. Speaking\n> of which, I remember keychain asking me \"do you want to let git see this\n> password?\", but I don't ever remember it asking \"do you want to save\n> this password?\". Is that usually automatic? Again, I was kind of\n> expecting a dialog with a \"remember this\" checkbox.\n\nEach keychain entry has an ACL of applications that are allowed to\naccess it. When an application asks for an entry and the application\nisn't on that entry's ACL, OS X (not the application) presents the\nuser the dialog you refer to. The application has no control over that\ndialog.\n\nNow, I could have the credential helper ask the user \"store this\npassword?\" after prompting for it, but why even use a credential\nhelper if you don't want it to store your credentials?\n\n>> +def add_internet_password(protocol, hostname, username, password):\n>> +    # We do this over a pipe so that we can provide the password more\n>> +    # securely than as an argument which would show up in ps output.\n>> +    # Unfortunately this is possibly less robust since the security man\n>> +    # page does not document how to quote arguments. Emprically it seems\n>> +    # that using the double-quote, escaping \\ and \" works properly.\n>> +    username = username.replace('\\\\', '\\\\\\\\').replace('\"', '\\\\\"')\n>> +    password = password.replace('\\\\', '\\\\\\\\').replace('\"', '\\\\\"')\n>> +    command = ' '.join([\n>> +        'add-internet-password', '-U',\n>> +        '-r', protocol,\n>> +        '-s', hostname,\n>> +        '-a \"%s\"' % username,\n>> +        '-w \"%s\"' % password,\n>> +        '-j default',\n>> +        '-l \"%s (%s)\"' % (hostname, username),\n>> +    ]) + '\\n'\n>> +    args = ['/usr/bin/security', '-i']\n>> +    p = Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE)\n>> +    p.communicate(command)\n>\n> I noticed that when using the python helper, the dialog asking something\n> like: \"security wants to know this password. Allow it?\"\n>\n> Which was kind of lame. I would hope we could convince it to say \"git\".\n> But I didn't see any option in the \"security\" tool for specifying the\n> context[1]. The C helper says \"git-credential-osxkeychain\". Which isn't\n> the end of the world, but it would be prettier if it just said \"git\".\n\nThat's partly why I wrote the C version.\n\n> [1] I can kind of see why they might not want you to set this for\n> security reasons (because it makes impersonating other programs easy).\n> On the other hand, saying \"security\" conveys absolutely nothing. And as\n> far as I can tell, I could just call my program /tmp/iTunes, and it\n> would say \"iTunes wants to know this password...\".\n\nIt is for security reasons. 99% of users will probably just click\n\"Okay\" no matter what. For the 1% that bother to pay attention to the\ndialog, it provides the full path to the binary. I'd be suspicious if\n/tmp/iTunes wanted a password.\n\nj.\n"},{"id":"176592","messageId":"CAG+J_DyhcA7RmHwgGJBw4r9JRij0_ONp3ZMD6oMTJ_f4dvYW8w@mail.gmail.com","threadId":"28393","inReplyTo":"CAEBDL5WhpVg17aPuRqrE5=2Q293kVD4fYtxGqRzx_K=87t-jgw@mail.gmail.com","subject":"Re: [PATCH] contrib: add a pair of credential helpers for Mac OS X's keychain","fromName":"Jay Soffian","fromEmail":"jaysoffian@gmail.com","sentAt":"2011-09-30T19:33:37Z","receivedAt":"2011-09-30T19:33:37Z","isPatch":true,"sender":{"key":"jaysoffian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/155970?v=4"},"body":"On Thu, Sep 29, 2011 at 6:03 AM, John Szakmeister <john@szakmeister.net> wrote:\n>\n> I've been working on a version of the keychain credential cache as\n> well.  I did create a gui, although it's a bit painful.\n\nI still don't understand why a CLI app should have a GUI credential prompt.\n\n>>  2. The \"get the username from the config\" feature is triggered at the\n>>     time of prompting the user (so instead of asking for the username,\n>>     we check the config and pretend the user told us).\n>>\n>>     I did it this way originally so that helpers would have the first\n>>     crack at setting a username, and we would fall back to the config.\n>>     Thinking on it more, that may be backwards. If the user has told\n>>     git \"for github.com, I am user 'foo'\", then that should probably\n>>     take effect first, and --username=foo get passed to the helper.\n\nSorry, missed this part in my previous reply. I don't understand - how\ndo you ever send a username to the credential helper if you don't get\nit from the config? But in any case, if you have a username (via\nconfig or some other way), yes, I think it should be given to the\ncredential helper.\n\n> I think that makes sense.  I think one thing we have to be careful\n> about partial matches.  I wouldn't want the credential cache to send\n> off the wrong password to a service.  This may be me being cautious,\n> but if I don't have all the necessary bits, I'd rather we fail that to\n> guess which entry is right.\n\nThe credential helper I wrote doesn't work that way. To do so would\nmean using a rather more complicated form of the OS X Security API. It\nasks for an entry using whatever fields it has, and OS X returns the\nfirst match that satisfies. It's up to the user to yea/nay that match\nif the credential helper isn't on the entry's ACL.\n\n>>> +     /* \"GitHub for Mac\" compatibility */\n>>> +     if (!strcmp(hostname, \"github.com\"))\n>>> +             hostname = \"github.com/mac\";\n>>\n>> Nice touch. :)\n>\n> I honestly don't understand why this needs to be done.\n\nBecause GitHub for Mac stores its entries using \"github.com/mac\" as\nthe hostname.\n\n> I don't use GitHub for Mac... does that mean this is busted for me?\n\nNo. It just means that the credential helper and GitHub for Mac store\ntheir entry in a compatible fashion. (So that each can locate the\nentry stored by the other.)\n\n> [snip]\n>> My series will also produce \"cert:/path/to/certificate\" when unlocking a\n>> certificate. The other candidates for conversion are smtp-auth (for\n>> send-email) and imap (for imap-send).  I guess for certs, you'd want to\n>> use the \"generic\" keychain type.\n>\n> There is a method for adding a certificate to the keychain:\n>   <http://developer.apple.com/library/mac/#documentation/Security/Reference/certifkeytrustservices/Reference/reference.html#//apple_ref/doc/uid/TP30000157>\n>\n> I'm not sure what that does exactly, but I do have a cert, and it\n> shows up as \"certificate\" in the keychain.\n\nThat's for storing a certificate itself. In this case, I think we're\njust talking about storing the passphrase which protects the\ncertificate's private key.\n\n>> I wonder if some people would not want to cache cert passwords. Speaking\n>> of which, I remember keychain asking me \"do you want to let git see this\n>> password?\", but I don't ever remember it asking \"do you want to save\n>> this password?\". Is that usually automatic? Again, I was kind of\n>> expecting a dialog with a \"remember this\" checkbox.\n>\n> By the time you get Keychain involved, the decision has been made.\n> Most applications offer that ability... and you're right, this should\n> probably offer the same capability.  That also means stashing that\n> data somewhere. :-(  OTOH, it does make for a better user experience.\n\nWhat, no? If you don't want git to store usernames/passwords stored in\nthe OS X Keychain, don't use the git-osx-keychain credential helper.\n\nj.\n"},{"id":"176602","messageId":"20110930221111.GB9384@sigill.intra.peff.net","threadId":"28393","inReplyTo":"CAG+J_DwntGc+j3duCVqsnoJGV18FqnwXJ99C1XqKope_zbGHAA@mail.gmail.com","subject":"Re: [PATCH] contrib: add a pair of credential helpers for Mac OS X's keychain","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2011-09-30T22:11:11Z","receivedAt":"2011-09-30T22:11:11Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Fri, Sep 30, 2011 at 03:16:58PM -0400, Jay Soffian wrote:\n\n> This makes no sense to me at all. Ignore OS X for the moment. You use\n> git on the command-line. Why would there be any expectation of it\n> interacting with the user via anything other than the terminal.\n\nBecause security prompts sometimes are out of band. In particular,\nyou're already interacting with the user outside of the terminal;\nopening the keychain will get you an \"allow git to open the keychain\"\ndialog.\n\nAnother example: if you're running gpg-agent, and you run \"git tag -s\",\nyou'll be prompted for your key passphrase in an out-of-band dialog.\n\nMaybe it doesn't make sense for the actual username/password, though.\n\n> I don't understand where you're running ssh from/to in this scenario,\n> but OS X has a notion of security contexts (this is a bit of a\n> tangent):\n> \n> http://developer.apple.com/library/mac/#technotes/tn2083/_index.html\n\nI meant running sshd on OS X, and then ssh-ing in from some other box.\nYour credential helper doesn't seem to work at all (I guess because it\nhas no access to the keychains). I don't think it's a big deal, though.\nIt's the minority case, and if somebody wants to figure out how to make\nit work later, they can.\n\n> I found it ugly that git's native getpass doesn't echo the username\n> back, and it seems hackish to me for the credential helper to turn\n> back around and invoke it in any case. :-(\n\nYes, but I think that's a bug that should be fixed in git. :)\n\nAs far as being hack-ish, the original design was that you could chain\nthese things if you wanted. But in practice, I don't know how useful\nthat is. In fact, after all of this discussion, I'm wondering how useful\nit is that the helpers are allowed to prompt themselves at all.\n\nThe KDE one does it. But would people be happier if git simply did:\n\n  1. ask the helper for a credential. if we get it, done\n\n  2. prompt the user\n\n  3. if the credential is valid, ask the helper to store\n\nThat's way less flexible. But it also makes the helpers really simple.\n\n> > My test harness checks that this case just asks for the password without\n> > bothering to do any lookup or storage. It probably doesn't really matter\n> > in practice; I think git should always be providing _some_ context.\n> \n> Okay, that wasn't clear from whatever documentation I read on how\n> credential helpers should behave. But why invoke the credential helper\n> just to ask for a password?\n\nBecause the helper might have an alternate way of asking for the\npassword (e.g., the KDE helper has its own dialog). Maybe it will never\nbe useful. I just wanted to future-proof helpers by giving them sane\nbehavior for this case, on the off chance that future versions of git do\nactually do this.\n\n> > Hrm. I was really hoping people wouldn't need to pick apart the \"unique\"\n> > token, and it could remain an opaque blob. If helpers are going to do\n> > this sort of parsing, then I'd just as soon have git break it down for\n> > them, and do something like:\n> >\n> >  git credential-osxkeychain \\\n> >    --protocol=https \\\n> >    --host=github.com \\\n> >    --path=peff/git.git\n> >    --username=peff\n> >\n> > to just hand over as much information as possible, and let the helper\n> > throw it all together if it wants to.\n> \n> Keychain entries have distinct fields. I broke apart the token and\n> stored it the way other applications mostly do on OS X.\n\nDon't get me wrong; I think you did the only sane thing. It was more\n\"Hmm, I was hoping that the right way to use the various security APIs\nwouldn't need to...\". And clearly my hope was wrong. :)\n\nBut this is exactly the sort of feedback I was hoping for; the interface\nto the helpers could be better, and we are catching it now.\n\n> > My series will also produce \"cert:/path/to/certificate\" when unlocking a\n> > certificate. The other candidates for conversion are smtp-auth (for\n> > send-email) and imap (for imap-send).  I guess for certs, you'd want to\n> > use the \"generic\" keychain type.\n> \n> Yep, I was punting on certificate for v1.\n\nNot unreasonable. Again, my real concern is not perfect helpers, but\nhaving helpers that exercise the helper interface enough so that we know\nwhether that interface is sufficient.\n\n> Each keychain entry has an ACL of applications that are allowed to\n> access it. When an application asks for an entry and the application\n> isn't on that entry's ACL, OS X (not the application) presents the\n> user the dialog you refer to. The application has no control over that\n> dialog.\n> \n> Now, I could have the credential helper ask the user \"store this\n> password?\" after prompting for it, but why even use a credential\n> helper if you don't want it to store your credentials?\n\nThat's not an unreasonable attitude. I mostly let the browser store\npasswords, but sometimes override it for specific sites. But in this\ncase, I think it would be more per-repo. And you can turn off the helper\nfor a particular repo (actually, I'm not sure you can, but you probably\nshould be able to).\n\n> It is for security reasons. 99% of users will probably just click\n> \"Okay\" no matter what. For the 1% that bother to pay attention to the\n> dialog, it provides the full path to the binary. I'd be suspicious if\n> /tmp/iTunes wanted a password.\n\nNot for me (on 10.7). Doing:\n\n  cp git-credential-osxkeychain /tmp/iTunes\n  /tmp/iTunes --unique=https:foo.tld\n\ngives me the dialog \"iTunes wants to access the 'login' keychain\" with a\npassword prompt.\n\nAnyway, that's neither here nor there. It would be nice if we could set\nthe text, but if we can't, then we'll have to live with it.\n\n-Peff\n"},{"id":"176603","messageId":"20110930221332.GC9384@sigill.intra.peff.net","threadId":"28393","inReplyTo":"CAG+J_DyhcA7RmHwgGJBw4r9JRij0_ONp3ZMD6oMTJ_f4dvYW8w@mail.gmail.com","subject":"Re: [PATCH] contrib: add a pair of credential helpers for Mac OS X's keychain","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2011-09-30T22:13:32Z","receivedAt":"2011-09-30T22:13:32Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Fri, Sep 30, 2011 at 03:33:37PM -0400, Jay Soffian wrote:\n\n> Sorry, missed this part in my previous reply. I don't understand - how\n> do you ever send a username to the credential helper if you don't get\n> it from the config? But in any case, if you have a username (via\n> config or some other way), yes, I think it should be given to the\n> credential helper.\n\nFor example:\n\n  git fetch https://user@host/repo.git\n  git fetch https://user2@host/repo.git\n\n-Peff\n"},{"id":"176607","messageId":"CAG+J_Dww1yOeq1LHQYMiObPKqrWbk4t8Hn=G9WpYWXFBbHiuhQ@mail.gmail.com","threadId":"28393","inReplyTo":"20110930221111.GB9384@sigill.intra.peff.net","subject":"Re: [PATCH] contrib: add a pair of credential helpers for Mac OS X's keychain","fromName":"Jay Soffian","fromEmail":"jaysoffian@gmail.com","sentAt":"2011-09-30T22:42:22Z","receivedAt":"2011-09-30T22:42:22Z","isPatch":true,"sender":{"key":"jaysoffian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/155970?v=4"},"body":"On Fri, Sep 30, 2011 at 6:11 PM, Jeff King <peff@peff.net> wrote:\n> Because security prompts sometimes are out of band. In particular,\n> you're already interacting with the user outside of the terminal;\n> opening the keychain will get you an \"allow git to open the keychain\"\n> dialog.\n\nUsually it won't. In the default case, the keychain is unlocked and no\npermission is needed to add an entry, nor to retrieve that entry by\nthe application which added it. The prompt will only occur if the\ncredential helper is not on the entry's ACL, or if the keychain is\nlocked.\n\n> Another example: if you're running gpg-agent, and you run \"git tag -s\",\n> you'll be prompted for your key passphrase in an out-of-band dialog.\n>\n> Maybe it doesn't make sense for the actual username/password, though.\n\nPersonally, it made sense to me do it at the CLI (obviously). But the\nsource code in in contrib now. :-)\n\n> I meant running sshd on OS X, and then ssh-ing in from some other box.\n> Your credential helper doesn't seem to work at all (I guess because it\n> has no access to the keychains). I don't think it's a big deal, though.\n> It's the minority case, and if somebody wants to figure out how to make\n> it work later, they can.\n\nAs far as I know there's no way to make this work. When you login over\nssh, it's a different security context, and there is no access to the\npassword field of keychain entries.\n\n>> I found it ugly that git's native getpass doesn't echo the username\n>> back, and it seems hackish to me for the credential helper to turn\n>> back around and invoke it in any case. :-(\n>\n> Yes, but I think that's a bug that should be fixed in git. :)\n\nYes it should. :-)\n\n> As far as being hack-ish, the original design was that you could chain\n> these things if you wanted. But in practice, I don't know how useful\n> that is. In fact, after all of this discussion, I'm wondering how useful\n> it is that the helpers are allowed to prompt themselves at all.\n>\n> The KDE one does it. But would people be happier if git simply did:\n>\n>  1. ask the helper for a credential. if we get it, done\n>\n>  2. prompt the user\n>\n>  3. if the credential is valid, ask the helper to store\n>\n> That's way less flexible. But it also makes the helpers really simple.\n\nI think that actually makes more sense. There's already an existing\nmechanism to customized (2) via GIT_ASKPASS, right? So it overlaps for\nthe credential helper to do that doesn't it?\n\n> Because the helper might have an alternate way of asking for the\n> password (e.g., the KDE helper has its own dialog). Maybe it will never\n> be useful. I just wanted to future-proof helpers by giving them sane\n> behavior for this case, on the off chance that future versions of git do\n> actually do this.\n>>\n> Don't get me wrong; I think you did the only sane thing. It was more\n> \"Hmm, I was hoping that the right way to use the various security APIs\n> wouldn't need to...\". And clearly my hope was wrong. :)\n>\n> But this is exactly the sort of feedback I was hoping for; the interface\n> to the helpers could be better, and we are catching it now.\n\nOkay, the more I think about this, the more I think the existing\ndesign is both too much (asking the credential helper to do anything\nother than store/retrieve passwords) and too little (not breaking out\nthe fields distinctly).\n\n> That's not an unreasonable attitude. I mostly let the browser store\n> passwords, but sometimes override it for specific sites. But in this\n> case, I think it would be more per-repo. And you can turn off the helper\n> for a particular repo (actually, I'm not sure you can, but you probably\n> should be able to).\n\nIf the credential helper becomes no more than a store/retrieve, it's\ngit that would do the prompting \"Store credentials via\ngit-osxkeychain?\" after logging in successfully with the credentials.\n\n>> It is for security reasons. 99% of users will probably just click\n\n> Anyway, that's neither here nor there. It would be nice if we could set\n> the text, but if we can't, then we'll have to live with it.\n\nThe C version of the helper will use the name of the helper binary.\n\nj.\n"},{"id":"176627","messageId":"CAEBDL5XhLAccfMoSyBjDA4ZsCgc4FnxEVYyqJsnGfzDW3Otudw@mail.gmail.com","threadId":"28393","inReplyTo":"CAG+J_DyhcA7RmHwgGJBw4r9JRij0_ONp3ZMD6oMTJ_f4dvYW8w@mail.gmail.com","subject":"Re: [PATCH] contrib: add a pair of credential helpers for Mac OS X's keychain","fromName":"John Szakmeister","fromEmail":"john@szakmeister.net","sentAt":"2011-10-01T06:57:50Z","receivedAt":"2011-10-01T06:57:50Z","isPatch":true,"sender":{"key":"john@szakmeister.net","avatar":"https://avatars.githubusercontent.com/u/448087?v=4"},"body":"On Fri, Sep 30, 2011 at 3:33 PM, Jay Soffian <jaysoffian@gmail.com> wrote:\n> On Thu, Sep 29, 2011 at 6:03 AM, John Szakmeister <john@szakmeister.net> wrote:\n>>\n>> I've been working on a version of the keychain credential cache as\n>> well.  I did create a gui, although it's a bit painful.\n>\n> I still don't understand why a CLI app should have a GUI credential prompt.\n\nFor one, I saw it more as being useful to things other than the git\ncommand line.  And the Mac already presents a GUI for unlocking the\nkeychain if necessary.  It's not like there isn't any precedent for\nit.\n\n[snip]\n>> I think that makes sense.  I think one thing we have to be careful\n>> about partial matches.  I wouldn't want the credential cache to send\n>> off the wrong password to a service.  This may be me being cautious,\n>> but if I don't have all the necessary bits, I'd rather we fail that to\n>> guess which entry is right.\n>\n> The credential helper I wrote doesn't work that way. To do so would\n> mean using a rather more complicated form of the OS X Security API. It\n> asks for an entry using whatever fields it has, and OS X returns the\n> first match that satisfies. It's up to the user to yea/nay that match\n> if the credential helper isn't on the entry's ACL.\n\nTrue, the user would at least have to acknowledge it.\n\n>>>> +     /* \"GitHub for Mac\" compatibility */\n>>>> +     if (!strcmp(hostname, \"github.com\"))\n>>>> +             hostname = \"github.com/mac\";\n>>>\n>>> Nice touch. :)\n>>\n>> I honestly don't understand why this needs to be done.\n>\n> Because GitHub for Mac stores its entries using \"github.com/mac\" as\n> the hostname.\n>\n>> I don't use GitHub for Mac... does that mean this is busted for me?\n>\n> No. It just means that the credential helper and GitHub for Mac store\n> their entry in a compatible fashion. (So that each can locate the\n> entry stored by the other.)\n\nAh, interesting.  But it does mean that it won't pick up the password\nI've cached via my browser, right?\n\n>> [snip]\n>>> My series will also produce \"cert:/path/to/certificate\" when unlocking a\n>>> certificate. The other candidates for conversion are smtp-auth (for\n>>> send-email) and imap (for imap-send).  I guess for certs, you'd want to\n>>> use the \"generic\" keychain type.\n>>\n>> There is a method for adding a certificate to the keychain:\n>>   <http://developer.apple.com/library/mac/#documentation/Security/Reference/certifkeytrustservices/Reference/reference.html#//apple_ref/doc/uid/TP30000157>\n>>\n>> I'm not sure what that does exactly, but I do have a cert, and it\n>> shows up as \"certificate\" in the keychain.\n>\n> That's for storing a certificate itself. In this case, I think we're\n> just talking about storing the passphrase which protects the\n> certificate's private key.\n\nI could've sworn the docs mentioned storing the private key, but I\ndon't see it.  SecIdentityCreateWithCertificate() can get you the\nprivate key from a cert, once added.  It's not clear how to go from\ncert to password in a manner that's compatible with other Mac apps.\n\n[snip]\n>> By the time you get Keychain involved, the decision has been made.\n>> Most applications offer that ability... and you're right, this should\n>> probably offer the same capability.  That also means stashing that\n>> data somewhere. :-(  OTOH, it does make for a better user experience.\n>\n> What, no? If you don't want git to store usernames/passwords stored in\n> the OS X Keychain, don't use the git-osx-keychain credential helper.\n\nI want *some* cached, and others not.  I don't want to be forced to\nremember to take git-osx-keychain out of my credentials list, or\nsomething silly like that.  That doesn't help on the user-friendliness\nfront.  Having a check box and storing the fact that we shouldn't\ncache the password in the repo's config seems like a reasonable\napproach.  But I agree with Jeff in another part of this thread: I\nthink the prompting of the username and password should be a separate\nmechanism.  It could prompt about whether or not to store the\npassword, and git could take care of whether or not to call the\ncredential store.\n\n-John\n"},{"id":"176733","messageId":"20111003105908.GF16078@sigill.intra.peff.net","threadId":"28393","inReplyTo":"CAG+J_Dww1yOeq1LHQYMiObPKqrWbk4t8Hn=G9WpYWXFBbHiuhQ@mail.gmail.com","subject":"Re: [PATCH] contrib: add a pair of credential helpers for Mac OS X's keychain","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2011-10-03T10:59:08Z","receivedAt":"2011-10-03T10:59:08Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Fri, Sep 30, 2011 at 06:42:22PM -0400, Jay Soffian wrote:\n\n> Usually it won't. In the default case, the keychain is unlocked and no\n> permission is needed to add an entry, nor to retrieve that entry by\n> the application which added it. The prompt will only occur if the\n> credential helper is not on the entry's ACL, or if the keychain is\n> locked.\n\nYeah. I was thinking the ACL prompt would come up more often, but I\nguess most people would hit \"allow always\", since it would get annoying\npretty quickly otherwise (I didn't, because I was testing).\n\nSide note: do you know how to edit those ACLs? I couldn't find it in the\nkeychain manager. It would be helpful for testing to be able to tweak it\n(as a workaround, I just modified the binary, which apparently the\nkeychain code cares about).\n\n> > Another example: if you're running gpg-agent, and you run \"git tag -s\",\n> > you'll be prompted for your key passphrase in an out-of-band dialog.\n> >\n> > Maybe it doesn't make sense for the actual username/password, though.\n> \n> Personally, it made sense to me do it at the CLI (obviously). But the\n> source code in in contrib now. :-)\n\nLet's leave it that way, then. It's open source, after all. If somebody\nreally wants a dialog, they can add it. They can even make it optional\n(you can do \"git config credential.helper 'osxkeychain --dialog'\" if the\nthing actually takes options).\n\n> >> I found it ugly that git's native getpass doesn't echo the username\n> >> back, and it seems hackish to me for the credential helper to turn\n> >> back around and invoke it in any case. :-(\n> >\n> > Yes, but I think that's a bug that should be fixed in git. :)\n> \n> Yes it should. :-)\n\nI think the only thing holding this back is portability. But it's awful\nto make reasonable platforms suffer because it can't be done portably.\nWe should at least do the right thing when we can, and fall back to\nusing getpass() otherwise. I'll see what I can do.\n\n> I think that actually makes more sense. There's already an existing\n> mechanism to customized (2) via GIT_ASKPASS, right? So it overlaps for\n> the credential helper to do that doesn't it?\n\nSort of. The askpass interface was really invented for asking for ssh\npassphrases. So:\n\n  1. It can only ask for one thing at a time. So you get two dialogs,\n     not one, and there's no place for anything else, like a \"remember\n     this password\" checkbox.\n\n  2. Like the getpass() hack in git, it won't show you what you've typed\n     so far for the username (there are several implementations, so it's\n     possible one could have an extra feature for this, but the ones\n     I've seen just always show some opaque image for each character\n     typed).\n\n  3. There's no way to pre-fill the username field, and let the user\n     override. So you end up just taking whatever username we already\n     have, ask for the password, and then try the authentication. The\n     user's only recourse is to restart the command with a different URL\n     (with the alternative username in the URL).\n\n> Okay, the more I think about this, the more I think the existing\n> design is both too much (asking the credential helper to do anything\n> other than store/retrieve passwords) and too little (not breaking out\n> the fields distinctly).\n\nI remember in some initial research that there may have been some system\nwhich really wanted to do the prompting itself. But now I can't find any\nreference to it. I think it may have been the freedesktop Secrets API,\nbut now that I read their documentation again, I think I may simply have\nbeen mistaking the prompt for unlocking the secure storage itself, not\nthe actual username and password.\n\n> > That's not an unreasonable attitude. I mostly let the browser store\n> > passwords, but sometimes override it for specific sites. But in this\n> > case, I think it would be more per-repo. And you can turn off the helper\n> > for a particular repo (actually, I'm not sure you can, but you probably\n> > should be able to).\n> \n> If the credential helper becomes no more than a store/retrieve, it's\n> git that would do the prompting \"Store credentials via\n> git-osxkeychain?\" after logging in successfully with the credentials.\n\nMaybe. I had assumed we would hand it off to the helper, and it would\nmake the decision (possibly after consulting with the user). But I\nsuppose git could do it itself.\n\n-Peff\n"},{"id":"176743","messageId":"CAG+J_DxAaw=vVENFUP5Mq9+inuDEpn_3Le_b7sO97wRUW6aFSA@mail.gmail.com","threadId":"28393","inReplyTo":"20111003105908.GF16078@sigill.intra.peff.net","subject":"Re: [PATCH] contrib: add a pair of credential helpers for Mac OS X's keychain","fromName":"Jay Soffian","fromEmail":"jaysoffian@gmail.com","sentAt":"2011-10-03T13:13:12Z","receivedAt":"2011-10-03T13:13:12Z","isPatch":true,"sender":{"key":"jaysoffian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/155970?v=4"},"body":"On Mon, Oct 3, 2011 at 6:59 AM, Jeff King <peff@peff.net> wrote:\n> On Fri, Sep 30, 2011 at 06:42:22PM -0400, Jay Soffian wrote:\n>\n>> Usually it won't. In the default case, the keychain is unlocked and no\n>> permission is needed to add an entry, nor to retrieve that entry by\n>> the application which added it. The prompt will only occur if the\n>> credential helper is not on the entry's ACL, or if the keychain is\n>> locked.\n>\n> Yeah. I was thinking the ACL prompt would come up more often, but I\n> guess most people would hit \"allow always\", since it would get annoying\n> pretty quickly otherwise (I didn't, because I was testing).\n\nIn the normal case, the keychain entry would be added via the\ncredential helper, so they'd never even see the prompt since the\nbinary which adds an entry is automatically on that entry's ACL.\n\n> Side note: do you know how to edit those ACLs? I couldn't find it in the\n> keychain manager. It would be helpful for testing to be able to tweak it\n> (as a workaround, I just modified the binary, which apparently the\n> keychain code cares about).\n\nDouble-click on the entry in Keychain Access, then click the \"Access\nControl\" tab.\n\nj.\n"},{"id":"176744","messageId":"CAG+J_DymMLP+c8kAqDOoPSGJG0CicZWzZPAO+D+cyqF5X19YHQ@mail.gmail.com","threadId":"28393","inReplyTo":"CAEBDL5XhLAccfMoSyBjDA4ZsCgc4FnxEVYyqJsnGfzDW3Otudw@mail.gmail.com","subject":"Re: [PATCH] contrib: add a pair of credential helpers for Mac OS X's keychain","fromName":"Jay Soffian","fromEmail":"jaysoffian@gmail.com","sentAt":"2011-10-03T13:16:42Z","receivedAt":"2011-10-03T13:16:42Z","isPatch":true,"sender":{"key":"jaysoffian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/155970?v=4"},"body":"On Sat, Oct 1, 2011 at 2:57 AM, John Szakmeister <john@szakmeister.net> wrote:\n>>> I don't use GitHub for Mac... does that mean this is busted for me?\n>>\n>> No. It just means that the credential helper and GitHub for Mac store\n>> their entry in a compatible fashion. (So that each can locate the\n>> entry stored by the other.)\n>\n> Ah, interesting.  But it does mean that it won't pick up the password\n> I've cached via my browser, right?\n\nCorrect. I can add code to also make it look for the password entry as\nstored by Safari/Chrome. It's actually stored as a slightly different\nentry type (\"Web form password\" vs \"Internet password\"), so it's not\njust the hostname difference.\n\nj.\n"},{"id":"176828","messageId":"20111004101610.GA11236@sigill.intra.peff.net","threadId":"28393","inReplyTo":"CAG+J_DxAaw=vVENFUP5Mq9+inuDEpn_3Le_b7sO97wRUW6aFSA@mail.gmail.com","subject":"Re: [PATCH] contrib: add a pair of credential helpers for Mac OS X's keychain","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2011-10-04T10:16:10Z","receivedAt":"2011-10-04T10:16:10Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Mon, Oct 03, 2011 at 09:13:12AM -0400, Jay Soffian wrote:\n\n> > Yeah. I was thinking the ACL prompt would come up more often, but I\n> > guess most people would hit \"allow always\", since it would get annoying\n> > pretty quickly otherwise (I didn't, because I was testing).\n> \n> In the normal case, the keychain entry would be added via the\n> credential helper, so they'd never even see the prompt since the\n> binary which adds an entry is automatically on that entry's ACL.\n\nAh, that makes sense. That wasn't what happened for me; the first time I\nran it, before I had ever given it a password, it asked if it could\naccess the login keychain. But that was because I _already_ had an entry\nthere from the GitHub for Mac client.\n\nSo I assume it was typical that most users would see it at least that\nfirst time. But it's probably not.\n\n> > Side note: do you know how to edit those ACLs? I couldn't find it in the\n> > keychain manager. It would be helpful for testing to be able to tweak it\n> > (as a workaround, I just modified the binary, which apparently the\n> > keychain code cares about).\n> \n> Double-click on the entry in Keychain Access, then click the \"Access\n> Control\" tab.\n\nThanks. For some reason I was thinking the ACL was based on the\nkeychain, but of course having it per-entry makes much more sense. So I\nwas just looking in the wrong place.\n\n-Peff\n"},{"id":"176855","messageId":"7v39f8d6iq.fsf@alter.siamese.dyndns.org","threadId":"28393","inReplyTo":"20111004101610.GA11236@sigill.intra.peff.net","subject":"Re: [PATCH] contrib: add a pair of credential helpers for Mac OS X's keychain","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-10-04T17:13:33Z","receivedAt":"2011-10-04T17:13:33Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Now, I would like to start moving the core part of the credential helper\nseries to 'master'. Could people summarize what the current status of\nvarious moving parts are?\n\nHere is an example of write-up, based on my understanding (I said it\nshould be pretty-much ready, but please correct me if I am mistaken).\n\n - The core part have seen some updates while it was cooking in 'next',\n   with help from inputs by credential plug-in authors. The API and the\n   sample implementation should be stable enough that it can be given to\n   people who follow 'master' perhaps with 'experimental, minor details\n   still subject to change' label attached.\n\nI'd like to see similar write-ups for plug-ins from people who were\ninvolved in the topic, at least on the following:\n\n - Mac OS X keychain?\n\n - Gnome?\n\n - KDE?\n\n - Windows?\n\n - Others?\n\nThanks.\n"},{"id":"176856","messageId":"20111004174840.GA31558@sigill.intra.peff.net","threadId":"28393","inReplyTo":"7v39f8d6iq.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH] contrib: add a pair of credential helpers for Mac OS X's keychain","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2011-10-04T17:48:40Z","receivedAt":"2011-10-04T17:48:40Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Tue, Oct 04, 2011 at 10:13:33AM -0700, Junio C Hamano wrote:\n\n> Now, I would like to start moving the core part of the credential helper\n> series to 'master'. Could people summarize what the current status of\n> various moving parts are?\n> \n> Here is an example of write-up, based on my understanding (I said it\n> should be pretty-much ready, but please correct me if I am mistaken).\n> \n>  - The core part have seen some updates while it was cooking in 'next',\n>    with help from inputs by credential plug-in authors. The API and the\n>    sample implementation should be stable enough that it can be given to\n>    people who follow 'master' perhaps with 'experimental, minor details\n>    still subject to change' label attached.\n\nNo, sadly I don't think we're there yet. The two big open questions are:\n\n  1. Should we be giving more context details to the helpers, and/or\n     should we be breaking down the information into pieces?\n\n     I think the answer is probably yes. Certainly OS X would benefit\n     from the broken-down pieces. My feeling is that we could hand\n     helpers both broken-down pieces as well as an inclusive URL. So\n     something like:\n\n       git credential-foo \\\n         --type=network --protocol=https --host=example.com \\\n         --username=user1 --path=repo.git \\\n         --url=https://user1@example.com/repo.git\n\n     and then the helper can pick what it likes from there.\n\n     One thing I haven't figured out is how the user would tell git \"no,\n     the repo path is not relevant for determining the auth domain\".\n     That feature can come later, but I want to make sure that helpers\n     know they might or might not get the \"--path\" option. I guess that\n     is just a matter of documentation; I'm just a little nervous\n     committing to it without having figured out the details.\n\n  2. There has been some talk that the helper interface should perhaps\n     be vastly simplified from \"get the credentials and give them to\n     git\" to merely being a store/retrieve system, where the invocations\n     would be something like (pretend git is the shell):\n\n       # git asks if we have a stored credential\n       git credential-foo --get --url=...,etc | read password\n\n       # we had a successful authentication; ask the helper to store it;\n       echo password | git credential-foo --put --url=...,etc\n\n     That makes the helpers much simpler, and makes interacting with the\n     user more uniform across helpers.\n\n     It disallows helpers doing specialized interactions or dialogs. I'm\n     not sure how much we want that. I thought some systems might find\n     that more natural, but that's debatable. Jay implemented a git-like\n     pure-terminal prompt in his helper. Lukas used the kde\n     password-dialog for the KDE helper. I'm not sure what people really want.\n\nI can produce patches for both if we want to see what they would look\nlike. But I kind of think it's not a matter of \"what will the code look\nlike\" (both of them are pretty straightforward to implement), but \"what\ndo we want the interface to look like\". And that is really as much up to\nme as to people who are going to write and use the helpers.\n\n-Peff\n"},{"id":"176864","messageId":"7vy5x0bmjk.fsf@alter.siamese.dyndns.org","threadId":"28393","inReplyTo":"20111004174840.GA31558@sigill.intra.peff.net","subject":"Re: [PATCH] contrib: add a pair of credential helpers for Mac OS X's keychain","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-10-04T19:10:23Z","receivedAt":"2011-10-04T19:10:23Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> No, sadly I don't think we're there yet.\n\nIt is much better to proceed with caution than unleashing something whose\nsemantics will later have to change so drastically that we end up hurting\nend users, so nobody has to say \"sadly\". By spelling out what more needs\nto be there for us to get there, we are making progress.\n\n> The two big open questions are:\n>\n>   1. Should we be giving more context details to the helpers, and/or\n>      should we be breaking down the information into pieces?\n>\n>      I think the answer is probably yes. Certainly OS X would benefit\n>      from the broken-down pieces. My feeling is that we could hand\n>      helpers both broken-down pieces as well as an inclusive URL. So\n>      something like:\n>\n>        git credential-foo \\\n>          --type=network --protocol=https --host=example.com \\\n>          --username=user1 --path=repo.git \\\n>          --url=https://user1@example.com/repo.git\n>\n>      and then the helper can pick what it likes from there.\n\nHmm, don't we first want to enumerate contexts where we might want to get\nthe access information from the user? E.g.\n\n * \"transport\" aka \"git fetch/push\"; I think you meant this by --type=network,\n   but there probably are other kinds of accesses over \"network\".\n * \"imap-send\".\n * \"send-email\".\n * \"tag -s\" and perhaps upcoming \"push --signed\" or \"commit --gpg-sign\"?\n\nAnything else?\n\nI am not sure where the unlocking passphrase for ssl cert fits in this\npicture, though.\n\n>      One thing I haven't figured out is how the user would tell git \"no,\n>      the repo path is not relevant for determining the auth domain\".\n>      That feature can come later, but I want to make sure that helpers\n>      know they might or might not get the \"--path\" option. I guess that\n>      is just a matter of documentation; I'm just a little nervous\n>      committing to it without having figured out the details.\n\nWell, shouldn't the caller of credential-foo be passing --authdomain as a\nseparate piece anyway?  In the above example, if example.com operates as a\nsingle auth domain no matter what repository, --authdomain may just say\n\"\". If it uses per repository auth domain, --authdomain may say \"repo.git\"\n(I am assuming <authdomain> is not globally unique but unique within the\ncontext of <type, host> or perhaps <type, proto, host>). \n\n>   2. There has been some talk that the helper interface should perhaps\n>      be vastly simplified from \"get the credentials and give them to\n>      git\" to merely being a store/retrieve system, where the invocations\n>      would be something like (pretend git is the shell):\n>\n>        # git asks if we have a stored credential\n>        git credential-foo --get --url=...,etc | read password\n>\n>        # we had a successful authentication; ask the helper to store it;\n>        echo password | git credential-foo --put --url=...,etc\n>\n>      That makes the helpers much simpler, and makes interacting with the\n>      user more uniform across helpers.\n>\n>      It disallows helpers doing specialized interactions or dialogs.\n\nVery true.\n\nI have this suspicion that running dialog on various desktop environments\nmay be orthogonal to what credential store to be used. Once we know the\nrepertoire of the broken-down pieces, we may want to add \"dialog-foo\"\nfamily of helpers whose sole purpose is to interact with the user to get\nmissing information.  E.g. we may notice that we need to ask both the\nusername and password, and invoke\n\n    $ dialog-foo --msg=$msg --ask=username --ask-secret=password\n\nwhere $msg tells the user why we are asking these pieces (e.g. trying to\npush into https://example.com/repo.git), and then read from the helper\nwhat the user gave it.\n\nIf the URL had user1@ in it, then the $msg would say that we are trying to\npush into https://example.com/repo.git as user1, and we invoke the helper\nwith only --ask-secret=password (without --ask=username).\n\nThe same \"helper\" executable _could_ implement both interfaces, but I\nthink they are logically separate.\n"},{"id":"176871","messageId":"CAG+J_DxQg_0YB8X6LMXhus817qRLLQkbCp939Ux4rzO36tj4Sg@mail.gmail.com","threadId":"28393","inReplyTo":"7vy5x0bmjk.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH] contrib: add a pair of credential helpers for Mac OS X's keychain","fromName":"Jay Soffian","fromEmail":"jaysoffian@gmail.com","sentAt":"2011-10-04T20:09:26Z","receivedAt":"2011-10-04T20:09:26Z","isPatch":true,"sender":{"key":"jaysoffian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/155970?v=4"},"body":"On Tue, Oct 4, 2011 at 3:10 PM, Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Hmm, don't we first want to enumerate contexts where we might want to get\n> the access information from the user? E.g.\n>\n>  * \"transport\" aka \"git fetch/push\"; I think you meant this by --type=network,\n>   but there probably are other kinds of accesses over \"network\".\n>  * \"imap-send\".\n>  * \"send-email\".\n>  * \"tag -s\" and perhaps upcoming \"push --signed\" or \"commit --gpg-sign\"?\n>\n> Anything else?\n\nPerhaps it would be illustrative to look at the OS X keychain API call\nfor adding a password to the store:\n\nOSStatus SecKeychainAddInternetPassword (\n   SecKeychainRef keychain,\n   UInt32 serverNameLength,\n   const char *serverName,\n   UInt32 securityDomainLength,\n   const char *securityDomain,\n   UInt32 accountNameLength,\n   const char *accountName,\n   UInt32 pathLength,\n   const char *path,\n   UInt16 port,\n   SecProtocolType protocol,\n   SecAuthenticationType authenticationType,\n   UInt32 passwordLength,\n   const void *passwordData,\n   SecKeychainItemRef *itemRef\n);\n\nSecProtocolType is an enum of 4-char values such as 'ftp ', 'http',\netc. Similarly for SecAuthenticationType which uses values such as\n'form' (web form), 'http' (basic auth), etc.\n\nhttp://developer.apple.com/library/mac/#documentation/Security/Reference/keychainservices/Reference/reference.html#//apple_ref/doc/c_ref/SecKeychainAddInternetPassword\nhttp://developer.apple.com/library/mac/#documentation/Security/Reference/keychainservices/Reference/reference.html#//apple_ref/doc/c_ref/SecProtocolType\nhttp://developer.apple.com/library/mac/#documentation/Security/Reference/keychainservices/Reference/reference.html#//apple_ref/doc/c_ref/SecAuthenticationType\n\nj.\n"}]}