From patchwork Sat Feb 17 23:34:53 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Bo Anderson X-Patchwork-Id: 13561621 Received: from mail-wm1-f54.google.com (mail-wm1-f54.google.com [209.85.128.54]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 96FA37FBCE for ; Sat, 17 Feb 2024 23:35:00 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.54 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1708212902; cv=none; b=QE5oE7pzYDYRF9jTOQBz1M2G31vqOsebH8sxQFynjjEkuC5tiwk8HatW6BvBglQCayF4nnV/F/vtHZmH5G7ANvUVWXn5/f1UQLrp7k8mMSJkI7vYbWsfEG6XuJOusJosdZr8sgjG8rjSPWJhvE/154nwPsTvqlOQH+Q/ts8cDzU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1708212902; c=relaxed/simple; bh=rJO5AFzlNWB2aLm6k6vOuTG9txMapd8R9nPOOCxb45g=; h=Message-ID:In-Reply-To:References:From:Date:Subject:Content-Type: MIME-Version:To:Cc; b=mQjhlXeAZQxAlp4t4fvnwNTp0NBVNpilbVMauBrqX9/tcjzZkkOceW8eth2DfqOkiBojLOXInQeg8FeSZ02LIHehLrBznyV55D+9XMOcEQCl1zDbS9avBMKsIDN8UZynHEwt2PzW292zJLf9iDCDKiwS5TxpYNhJ3w/CB0zH8qM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=I0PTnQpL; arc=none smtp.client-ip=209.85.128.54 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="I0PTnQpL" Received: by mail-wm1-f54.google.com with SMTP id 5b1f17b1804b1-41259f06e94so6160505e9.2 for ; Sat, 17 Feb 2024 15:35:00 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1708212898; x=1708817698; darn=vger.kernel.org; h=cc:to:mime-version:content-transfer-encoding:fcc:subject:date:from :references:in-reply-to:message-id:from:to:cc:subject:date :message-id:reply-to; bh=HZCvLYdvpjXcv4tsrMvKoLKxHPlI73UmauNOCTcslBQ=; b=I0PTnQpLu6ZoQ6cVlaDo2JE9t1X4bBvvSR2Fy/rpWRcU6kyQyIP1ttKU0eOFMkXc1M Nh2cy9eTpmdhBe4L6kz0hG/f4HJD7aaxdP1UjHllI6W59+i8pW9Rc/btLe4CDVr8YHkV K9IoEacjRxJGini0L55LoCIKs1uOirRqQiLCKyOVAqIh8PZSP4+FyUQmwE4cU49o5KPd 51BkSq0t/qeQfF34haLKupV1ezJhB+YdbdfI6fUeFEbJGSxwY81Duby7xXLDGANfDfqb srl9Fv0XrNVTxbVikC6v00vUbD0I6xkERbAXfahQFiwGmmhJp0qMF8I9mAjh7goNiUwq nA+Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1708212898; x=1708817698; h=cc:to:mime-version:content-transfer-encoding:fcc:subject:date:from :references:in-reply-to:message-id:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=HZCvLYdvpjXcv4tsrMvKoLKxHPlI73UmauNOCTcslBQ=; b=oIVpPDU4bbVXtd2/egRvUGDdaYQgs7CmM+y39o2GrY3TJrxmrjJwQEF8QEBqJC7BCV 362vTwlQvQZQ8cwLLzRi3E5d+Df0hxEDC/m6TO8Zvv1b1yA/pWwdhkh0+vKMZ4zmL0LW 1W4FFpdiDz5RsEjkm2catfT5nQvX3SyslRStluKf98giouBMmI8jWjgZjOqcGXbv1mrU acBanG47QJCSf34iS0eQodWVeso2q4k1WA4Hn9WMAAgR01ar4e23QUjLpcSMnT0WS0Dv 0rgt0jhUtXyeENVET9TLd+bmoKVBfq5zYFXncLzKMyKCqYo1cINNbrTlHE+jHw4G7f0E f5aA== X-Gm-Message-State: AOJu0YwR2GaihsfAX5UEd1M9NHwIvU3UYxDoUHxsleAOvYO0G13oxUKC Fadh6z208l7zrejTx4UROCPkRgtiCvQNPtv9KOjfI7I2T6ecHn79oAtl30pX X-Google-Smtp-Source: AGHT+IGLGU8BzZvnbO6pLGH7jrP4GlonPNtHaMFycRkukXAOD7VBfbe6wgXexbitz3dYKayJN5qsmA== X-Received: by 2002:a05:600c:450f:b0:412:6015:3dbd with SMTP id t15-20020a05600c450f00b0041260153dbdmr571885wmo.4.1708212898069; Sat, 17 Feb 2024 15:34:58 -0800 (PST) Received: from [127.0.0.1] ([13.74.141.28]) by smtp.gmail.com with ESMTPSA id f7-20020a5d5687000000b0033ce727e728sm5911666wrv.94.2024.02.17.15.34.57 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 17 Feb 2024 15:34:57 -0800 (PST) Message-ID: In-Reply-To: References: Date: Sat, 17 Feb 2024 23:34:53 +0000 Subject: [PATCH 1/4] osxkeychain: replace deprecated SecKeychain API Fcc: Sent Precedence: bulk X-Mailing-List: git@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 To: git@vger.kernel.org Cc: Bo Anderson , Bo Anderson From: Bo Anderson From: Bo Anderson The SecKeychain API was deprecated in macOS 10.10, nearly 10 years ago. The replacement SecItem API however is available as far back as macOS 10.6. While supporting older macOS was perhaps prevously a concern, git-credential-osxkeychain already requires a minimum of macOS 10.7 since 5747c8072b (contrib/credential: avoid fixed-size buffer in osxkeychain, 2023-05-01) so using the newer API should not regress the range of macOS versions supported. Adapting to use the newer SecItem API also happens to fix two test failures in osxkeychain: 8 - helper (osxkeychain) overwrites on store 9 - helper (osxkeychain) can forget host The new API is compatible with credentials saved with the older API. Signed-off-by: Bo Anderson --- contrib/credential/osxkeychain/Makefile | 3 +- .../osxkeychain/git-credential-osxkeychain.c | 265 +++++++++++++----- 2 files changed, 199 insertions(+), 69 deletions(-) diff --git a/contrib/credential/osxkeychain/Makefile b/contrib/credential/osxkeychain/Makefile index 4b3a08a2bac..238f5f8c36f 100644 --- a/contrib/credential/osxkeychain/Makefile +++ b/contrib/credential/osxkeychain/Makefile @@ -8,7 +8,8 @@ CFLAGS = -g -O2 -Wall -include ../../../config.mak git-credential-osxkeychain: git-credential-osxkeychain.o - $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) -Wl,-framework -Wl,Security + $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) \ + -framework Security -framework CoreFoundation git-credential-osxkeychain.o: git-credential-osxkeychain.c $(CC) -c $(CFLAGS) $< diff --git a/contrib/credential/osxkeychain/git-credential-osxkeychain.c b/contrib/credential/osxkeychain/git-credential-osxkeychain.c index 5f2e5f16c88..dc294ae944a 100644 --- a/contrib/credential/osxkeychain/git-credential-osxkeychain.c +++ b/contrib/credential/osxkeychain/git-credential-osxkeychain.c @@ -3,14 +3,39 @@ #include #include -static SecProtocolType protocol; -static char *host; -static char *path; -static char *username; -static char *password; -static UInt16 port; - -__attribute__((format (printf, 1, 2))) +#define ENCODING kCFStringEncodingUTF8 +static CFStringRef protocol; /* Stores constant strings - not memory managed */ +static CFStringRef host; +static CFStringRef path; +static CFStringRef username; +static CFDataRef password; +static CFNumberRef port; + +static void clear_credential(void) +{ + if (host) { + CFRelease(host); + host = NULL; + } + if (path) { + CFRelease(path); + path = NULL; + } + if (username) { + CFRelease(username); + username = NULL; + } + if (password) { + CFRelease(password); + password = NULL; + } + if (port) { + CFRelease(port); + port = NULL; + } +} + +__attribute__((format (printf, 1, 2), __noreturn__)) static void die(const char *err, ...) { char msg[4096]; @@ -19,70 +44,135 @@ static void die(const char *err, ...) vsnprintf(msg, sizeof(msg), err, params); fprintf(stderr, "%s\n", msg); va_end(params); + clear_credential(); exit(1); } -static void *xstrdup(const char *s1) +static void *xmalloc(size_t len) { - void *ret = strdup(s1); + void *ret = malloc(len); if (!ret) die("Out of memory"); return ret; } -#define KEYCHAIN_ITEM(x) (x ? strlen(x) : 0), x -#define KEYCHAIN_ARGS \ - NULL, /* default keychain */ \ - KEYCHAIN_ITEM(host), \ - 0, NULL, /* account domain */ \ - KEYCHAIN_ITEM(username), \ - KEYCHAIN_ITEM(path), \ - port, \ - protocol, \ - kSecAuthenticationTypeDefault - -static void write_item(const char *what, const char *buf, int len) +static CFDictionaryRef create_dictionary(CFAllocatorRef allocator, ...) +{ + va_list args; + const void *key; + CFMutableDictionaryRef result; + + result = CFDictionaryCreateMutable(allocator, + 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + + + va_start(args, allocator); + while ((key = va_arg(args, const void *)) != NULL) { + const void *value; + value = va_arg(args, const void *); + if (value) + CFDictionarySetValue(result, key, value); + } + va_end(args); + + return result; +} + +#define CREATE_SEC_ATTRIBUTES(...) \ + create_dictionary(kCFAllocatorDefault, \ + kSecClass, kSecClassInternetPassword, \ + kSecAttrServer, host, \ + kSecAttrAccount, username, \ + kSecAttrPath, path, \ + kSecAttrPort, port, \ + kSecAttrProtocol, protocol, \ + kSecAttrAuthenticationType, \ + kSecAttrAuthenticationTypeDefault, \ + __VA_ARGS__); + +static void write_item(const char *what, const char *buf, size_t len) { printf("%s=", what); fwrite(buf, 1, len, stdout); putchar('\n'); } -static void find_username_in_item(SecKeychainItemRef item) +static void find_username_in_item(CFDictionaryRef item) { - SecKeychainAttributeList list; - SecKeychainAttribute attr; + CFStringRef account_ref; + char *username_buf; + CFIndex buffer_len; - list.count = 1; - list.attr = &attr; - attr.tag = kSecAccountItemAttr; + account_ref = CFDictionaryGetValue(item, kSecAttrAccount); + if (!account_ref) + { + write_item("username", "", 0); + return; + } - if (SecKeychainItemCopyContent(item, NULL, &list, NULL, NULL)) + username_buf = (char *)CFStringGetCStringPtr(account_ref, ENCODING); + if (username_buf) + { + write_item("username", username_buf, strlen(username_buf)); return; + } - write_item("username", attr.data, attr.length); - SecKeychainItemFreeContent(&list, NULL); + /* If we can't get a CString pointer then + * we need to allocate our own buffer */ + buffer_len = CFStringGetMaximumSizeForEncoding( + CFStringGetLength(account_ref), ENCODING) + 1; + username_buf = xmalloc(buffer_len); + if (CFStringGetCString(account_ref, + username_buf, + buffer_len, + ENCODING)) { + write_item("username", username_buf, buffer_len - 1); + } + free(username_buf); } -static void find_internet_password(void) +static OSStatus find_internet_password(void) { - void *buf; - UInt32 len; - SecKeychainItemRef item; + CFDictionaryRef attrs; + CFDictionaryRef item; + CFDataRef data; + OSStatus result; - if (SecKeychainFindInternetPassword(KEYCHAIN_ARGS, &len, &buf, &item)) - return; + attrs = CREATE_SEC_ATTRIBUTES(kSecMatchLimit, kSecMatchLimitOne, + kSecReturnAttributes, kCFBooleanTrue, + kSecReturnData, kCFBooleanTrue, + NULL); + result = SecItemCopyMatching(attrs, (CFTypeRef *)&item); + if (result) { + goto out; + } - write_item("password", buf, len); + data = CFDictionaryGetValue(item, kSecValueData); + + write_item("password", + (const char *)CFDataGetBytePtr(data), + CFDataGetLength(data)); if (!username) find_username_in_item(item); - SecKeychainItemFreeContent(NULL, buf); + CFRelease(item); + +out: + CFRelease(attrs); + + /* We consider not found to not be an error */ + if (result == errSecItemNotFound) + result = errSecSuccess; + + return result; } -static void delete_internet_password(void) +static OSStatus delete_internet_password(void) { - SecKeychainItemRef item; + CFDictionaryRef attrs; + OSStatus result; /* * Require at least a protocol and host for removal, which is what git @@ -90,25 +180,42 @@ static void delete_internet_password(void) * Keychain manager. */ if (!protocol || !host) - return; + return -1; - if (SecKeychainFindInternetPassword(KEYCHAIN_ARGS, 0, NULL, &item)) - return; + attrs = CREATE_SEC_ATTRIBUTES(NULL); + result = SecItemDelete(attrs); + CFRelease(attrs); + + /* We consider not found to not be an error */ + if (result == errSecItemNotFound) + result = errSecSuccess; - SecKeychainItemDelete(item); + return result; } -static void add_internet_password(void) +static OSStatus add_internet_password(void) { + CFDictionaryRef attrs; + OSStatus result; + /* Only store complete credentials */ if (!protocol || !host || !username || !password) - return; + return -1; - if (SecKeychainAddInternetPassword( - KEYCHAIN_ARGS, - KEYCHAIN_ITEM(password), - NULL)) - return; + attrs = CREATE_SEC_ATTRIBUTES(kSecValueData, password, + NULL); + + result = SecItemAdd(attrs, NULL); + if (result == errSecDuplicateItem) { + CFDictionaryRef query; + query = CREATE_SEC_ATTRIBUTES(NULL); + result = SecItemUpdate(query, attrs); + CFRelease(query); + } + + CFRelease(attrs); + + return result; } static void read_credential(void) @@ -131,36 +238,52 @@ static void read_credential(void) if (!strcmp(buf, "protocol")) { if (!strcmp(v, "imap")) - protocol = kSecProtocolTypeIMAP; + protocol = kSecAttrProtocolIMAP; else if (!strcmp(v, "imaps")) - protocol = kSecProtocolTypeIMAPS; + protocol = kSecAttrProtocolIMAPS; else if (!strcmp(v, "ftp")) - protocol = kSecProtocolTypeFTP; + protocol = kSecAttrProtocolFTP; else if (!strcmp(v, "ftps")) - protocol = kSecProtocolTypeFTPS; + protocol = kSecAttrProtocolFTPS; else if (!strcmp(v, "https")) - protocol = kSecProtocolTypeHTTPS; + protocol = kSecAttrProtocolHTTPS; else if (!strcmp(v, "http")) - protocol = kSecProtocolTypeHTTP; + protocol = kSecAttrProtocolHTTP; else if (!strcmp(v, "smtp")) - protocol = kSecProtocolTypeSMTP; - else /* we don't yet handle other protocols */ + protocol = kSecAttrProtocolSMTP; + else { + /* we don't yet handle other protocols */ + clear_credential(); exit(0); + } } else if (!strcmp(buf, "host")) { char *colon = strchr(v, ':'); if (colon) { + UInt16 port_i; *colon++ = '\0'; - port = atoi(colon); + port_i = atoi(colon); + port = CFNumberCreate(kCFAllocatorDefault, + kCFNumberShortType, + &port_i); } - host = xstrdup(v); + host = CFStringCreateWithCString(kCFAllocatorDefault, + v, + ENCODING); } else if (!strcmp(buf, "path")) - path = xstrdup(v); + path = CFStringCreateWithCString(kCFAllocatorDefault, + v, + ENCODING); else if (!strcmp(buf, "username")) - username = xstrdup(v); + username = CFStringCreateWithCString( + kCFAllocatorDefault, + v, + ENCODING); else if (!strcmp(buf, "password")) - password = xstrdup(v); + password = CFDataCreate(kCFAllocatorDefault, + (UInt8 *)v, + strlen(v)); /* * Ignore other lines; we don't know what they mean, but * this future-proofs us when later versions of git do @@ -173,6 +296,7 @@ static void read_credential(void) int main(int argc, const char **argv) { + OSStatus result = 0; const char *usage = "usage: git credential-osxkeychain "; @@ -182,12 +306,17 @@ int main(int argc, const char **argv) read_credential(); if (!strcmp(argv[1], "get")) - find_internet_password(); + result = find_internet_password(); else if (!strcmp(argv[1], "store")) - add_internet_password(); + result = add_internet_password(); else if (!strcmp(argv[1], "erase")) - delete_internet_password(); + result = delete_internet_password(); /* otherwise, ignore unknown action */ + if (result) + die("failed to %s: %d", argv[1], (int)result); + + clear_credential(); + return 0; } From patchwork Sat Feb 17 23:34:54 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Bo Anderson X-Patchwork-Id: 13561622 Received: from mail-lj1-f178.google.com (mail-lj1-f178.google.com [209.85.208.178]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3A7C380042 for ; Sat, 17 Feb 2024 23:35:02 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.208.178 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1708212903; cv=none; b=YCUAOvGCyp/qqVrdkZS/yo6h172NzE+zbpgEMkuKVWaqLx4i7m1zz3/TFuYsN1lXDeZzsV206xTh9VPuwqeIpkbx5LlsqIbdYoHhkRu9cpDH1PFIpp87g0REm20yokb0tDyT0X3OaCm5nOM/JkbSsS8CfwlTHYptUUCd+rkw2Kk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1708212903; c=relaxed/simple; bh=gs6nnzFRGCUkPVsUkhaMnRYwHqjo2+l7oRX6zD+wDCA=; h=Message-ID:In-Reply-To:References:From:Date:Subject:Content-Type: MIME-Version:To:Cc; b=jKl3olQOMXwn1/RoRmOeAHWRGZWAPENWFMKxKjoZf9w5oMqEX9H0TeFzDLCDA563U8JJeSvc4rstWSp3os9zazvGEHyJrnqYYMuIAWeqNLanFHbPUwuDVGJJlmvRwUQ1xVv3orlMXXk3yO9/kvUM0rfSY4hNxMeYuCW4tt8Q2GI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=nBSRwuMg; arc=none smtp.client-ip=209.85.208.178 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="nBSRwuMg" Received: by mail-lj1-f178.google.com with SMTP id 38308e7fff4ca-2d1094b5568so42894651fa.1 for ; Sat, 17 Feb 2024 15:35:01 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1708212900; x=1708817700; darn=vger.kernel.org; h=cc:to:mime-version:content-transfer-encoding:fcc:subject:date:from :references:in-reply-to:message-id:from:to:cc:subject:date :message-id:reply-to; bh=ym+cgvQe/zgPnTgRyIPa1VB8AyqUW1kZLwDJyJ3v8LQ=; b=nBSRwuMgDyiwCyI/bEJxI2IdRmj1An+2jb5xPIVQ7nDaWLqOqxQ00fwpvrtrAiuOIA kGe8cV9kxCODXR47EjvZRxj+R7YtyUuD/PJahIielEU4XLiw1XpCTSkDPnj4omNXNlpc E6TMsaiSX433MhSQO0uf+ouMvJ8bNJTK72agmNM7hQRdvpjydvu7bTs3mFza4SfgmN/A QcICEWMxkxoenqcMBPvmHlLwicrNgYIdAPmYUTWz0NXXgJn8mJGvNa/MYVkDGnyElf5u Xr8ahf2ilzjNJob9ebpAaP7+v2k8baqN4P9sBTMHSO31/Q41sCbqZgMK+jQ1dY4105ge hmhg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1708212900; x=1708817700; h=cc:to:mime-version:content-transfer-encoding:fcc:subject:date:from :references:in-reply-to:message-id:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=ym+cgvQe/zgPnTgRyIPa1VB8AyqUW1kZLwDJyJ3v8LQ=; b=xU5N3OV7A6TC+zWS9N2hjUWW6boH+B3A+hYa4zezAoMi5MVGW3o1/yjVz7oRXAHRF9 UJkmT4ZokIs+p6/ByNcIr+6qu1ZWbYaLYlHbZbKZFxF05X5iDolmr46frbwqitwHdGg/ yGz+tGCPdxXVXBoysK6EwK9P15+FAKGGQrNzhLVZX22P4dtEgLgIXWe21g9CnTmYh9iw fpitiJrGLlQSwggdW7k6dIH+9GC7NI/k//JoU/vr17uTpl4AaABYZflUi3l3JDw55Avl wl2T6J3qcu8nWWsXHttPZkmPbbgh6Qv4Yd6cQJLLeqPjVpAu/Hs81WAl84jLdrHmnr8J LboQ== X-Gm-Message-State: AOJu0YzCjUZHi/qO77dXinnBTHLkZSoSt1XNKapnTX1e5HmPL9MzAW17 dm7aJqu/mJanW7sMfXpR+fSaXiITQx8weGmaXc/npiJr/cENvJgtKEgY9Kj6 X-Google-Smtp-Source: AGHT+IGOqdFw0koXGOW2RicQSx6s/ASv/ePU/i1JFeXCvXZPHt4wbkFpjFFbglhcFxgGNbE0yZHMDQ== X-Received: by 2002:a2e:2243:0:b0:2d2:298a:dfc8 with SMTP id i64-20020a2e2243000000b002d2298adfc8mr1431561lji.47.1708212899488; Sat, 17 Feb 2024 15:34:59 -0800 (PST) Received: from [127.0.0.1] ([13.74.141.28]) by smtp.gmail.com with ESMTPSA id 6-20020a05600c22c600b004120537210esm6460538wmg.46.2024.02.17.15.34.58 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 17 Feb 2024 15:34:58 -0800 (PST) Message-ID: <08284fa8e845e28da3c9a85d06475d5fbeb5cfcb.1708212896.git.gitgitgadget@gmail.com> In-Reply-To: References: Date: Sat, 17 Feb 2024 23:34:54 +0000 Subject: [PATCH 2/4] osxkeychain: erase all matching credentials Fcc: Sent Precedence: bulk X-Mailing-List: git@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 To: git@vger.kernel.org Cc: Bo Anderson , Bo Anderson From: Bo Anderson From: Bo Anderson Other credential managers erased all matching credentials, as indicated by a test case that osxkeychain failed: 15 - helper (osxkeychain) erases all matching credentials Signed-off-by: Bo Anderson --- contrib/credential/osxkeychain/git-credential-osxkeychain.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/contrib/credential/osxkeychain/git-credential-osxkeychain.c b/contrib/credential/osxkeychain/git-credential-osxkeychain.c index dc294ae944a..e9cee3aed45 100644 --- a/contrib/credential/osxkeychain/git-credential-osxkeychain.c +++ b/contrib/credential/osxkeychain/git-credential-osxkeychain.c @@ -182,7 +182,8 @@ static OSStatus delete_internet_password(void) if (!protocol || !host) return -1; - attrs = CREATE_SEC_ATTRIBUTES(NULL); + attrs = CREATE_SEC_ATTRIBUTES(kSecMatchLimit, kSecMatchLimitAll, + NULL); result = SecItemDelete(attrs); CFRelease(attrs); From patchwork Sat Feb 17 23:34:55 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Bo Anderson X-Patchwork-Id: 13561623 Received: from mail-wr1-f47.google.com (mail-wr1-f47.google.com [209.85.221.47]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B508A8004A for ; Sat, 17 Feb 2024 23:35:02 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.47 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1708212904; cv=none; b=JfjlimHOgYW4tl63IOEgCjtwNbE1vOetUh3ZKN3hbHeFfj/bD0QQ1N2ZUnhHlEYb2irhOW8mHTeXFI0kNRp/+nCZ1i0uZnaEXqLzIdZGTKqm2+iF8yeMlDFsGanmar/m8OglWpHjsrS5c1TJXJx5e+Sf9RbmnVqqh5Mo3iHwe2g= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1708212904; c=relaxed/simple; bh=F1NycC4EbjBByTSH5lB0vzLaycThwC2dpbQy+fLogFs=; h=Message-ID:In-Reply-To:References:From:Date:Subject:Content-Type: MIME-Version:To:Cc; b=sZrJdU2MHWccCXdOZvNfo/Ihreetmiz2Ei/TMbEJ0gVoQr8iGjfqaktDeyAt78ZP148AiKr5Pv25RSB6MVn/yta+NKUSE2xZMwE29ktWtRHwAK+2pBrGD8fQaYQSkMl7DzhNJdrU0NIHFMsIhQBmqnzuRV8I0r/r5qYwRSSlnBI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=IfAXApXn; arc=none smtp.client-ip=209.85.221.47 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="IfAXApXn" Received: by mail-wr1-f47.google.com with SMTP id ffacd0b85a97d-33d44d78e5fso6659f8f.1 for ; Sat, 17 Feb 2024 15:35:02 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1708212900; x=1708817700; darn=vger.kernel.org; h=cc:to:mime-version:content-transfer-encoding:fcc:subject:date:from :references:in-reply-to:message-id:from:to:cc:subject:date :message-id:reply-to; bh=l6cJKvxkeDZHNTPChkIA6Cm3kCYYihCcNkZPxmSdbj8=; b=IfAXApXn924hic4Pr/egFf5Zb3i+1+izGVSDihmoCaYpt6w8+dUSs6cnJSBkq4jIRB 8pD/CJSzfkGBMASjP3P0zg5dbwmssw/ZcXSxW4AZ35v8EGrTU6PvWmyow1YCK9qSqRcz Ky1zjQyEEPdzCEz4/JzsAFkKp52/H1q0y+ib7vwI1MelQP5ZCzbFKv3+CK+8q6vC2GY7 0Jx5G1RHWzUXRPWbwdmbNdM6fO8f4z95orsxJnkfukXCERmCrviFgTt76ef42G3283wj JN2rHgCk4rqS476QrPAmMktuvMsSjpSG7zTNvc3y7ofjdD04bv8ujbkvcm8dFBuTpSHg gWUw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1708212900; x=1708817700; h=cc:to:mime-version:content-transfer-encoding:fcc:subject:date:from :references:in-reply-to:message-id:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=l6cJKvxkeDZHNTPChkIA6Cm3kCYYihCcNkZPxmSdbj8=; b=URJIDQr8FQTQ/aHKaN29WCE8YuvClJe2/Tiw8Mq7SSRnoqpyf5Frbq9DNpEP4K7zvN hRmGzLmUVZjq1GAFppfNRQZA6PdgwSWjokDB+OZwuqge0pjpASVPnChT4y8vPkPiUbOQ rzJ88v2OTOonEIJU69K9AMbUHWnWFza6wfPYxXqEri2kiNZzx6Vgp+2L5kh0MbGjbuXz +9+verYRgI/WXEh5YkHu0rsy0A5y4RuWobJRzlRkuoq10wkzXlKBZKKGSKQSSK9XVgXs yM0B8WuN7z35QdJq2kIrKVu7wo2pBuKNKHoK9T8hp2U2+w0pC9Dv3N/aT7tv7mlBF3as yBFg== X-Gm-Message-State: AOJu0YyfjbEqxDL/B1JFz5rKubySAn5Y0j83r9s1qxL9+BrmQZBie1uA uf++lavdHtTcnzhQa0iApPA1ND5kYHwEoAvD5QnIVRPpnFfHLcsvxIzEXeMd X-Google-Smtp-Source: AGHT+IG0MDxLgcaFqwb++nTBtb10EZCqCKpslv9htcUlef5BvmYuunZKGGJKE+xRl2QCq1a0JEc/QQ== X-Received: by 2002:a5d:4c85:0:b0:33d:3cea:847e with SMTP id z5-20020a5d4c85000000b0033d3cea847emr293400wrs.21.1708212900029; Sat, 17 Feb 2024 15:35:00 -0800 (PST) Received: from [127.0.0.1] ([13.74.141.28]) by smtp.gmail.com with ESMTPSA id cc13-20020a5d5c0d000000b0033d20b430c3sm5251447wrb.115.2024.02.17.15.34.59 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 17 Feb 2024 15:34:59 -0800 (PST) Message-ID: In-Reply-To: References: Date: Sat, 17 Feb 2024 23:34:55 +0000 Subject: [PATCH 3/4] osxkeychain: erase matching passwords only Fcc: Sent Precedence: bulk X-Mailing-List: git@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 To: git@vger.kernel.org Cc: Bo Anderson , Bo Anderson From: Bo Anderson From: Bo Anderson Other credential helpers support deleting credentials that match a specified password. See 7144dee3ec (credential/libsecret: erase matching creds only, 2023-07-26) and cb626f8e5c (credential/wincred: erase matching creds only, 2023-07-26). Support this in osxkeychain too by extracting, decrypting and comparing the stored password before deleting. Fixes the following test failure with osxkeychain: 11 - helper (osxkeychain) does not erase a password distinct from input Signed-off-by: Bo Anderson --- .../osxkeychain/git-credential-osxkeychain.c | 56 ++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/contrib/credential/osxkeychain/git-credential-osxkeychain.c b/contrib/credential/osxkeychain/git-credential-osxkeychain.c index e9cee3aed45..9e742796336 100644 --- a/contrib/credential/osxkeychain/git-credential-osxkeychain.c +++ b/contrib/credential/osxkeychain/git-credential-osxkeychain.c @@ -169,9 +169,55 @@ static OSStatus find_internet_password(void) return result; } +static OSStatus delete_ref(const void *itemRef) +{ + CFArrayRef item_ref_list; + CFDictionaryRef delete_query; + OSStatus result; + + item_ref_list = CFArrayCreate(kCFAllocatorDefault, + &itemRef, + 1, + &kCFTypeArrayCallBacks); + delete_query = create_dictionary(kCFAllocatorDefault, + kSecClass, kSecClassInternetPassword, + kSecMatchItemList, item_ref_list, + NULL); + + if (password) { + /* We only want to delete items with a matching password */ + CFIndex capacity; + CFMutableDictionaryRef query; + CFDataRef data; + + capacity = CFDictionaryGetCount(delete_query) + 1; + query = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, + capacity, + delete_query); + CFDictionarySetValue(query, kSecReturnData, kCFBooleanTrue); + result = SecItemCopyMatching(query, (CFTypeRef *)&data); + if (!result) { + if (CFEqual(data, password)) + result = SecItemDelete(delete_query); + + CFRelease(data); + } + + CFRelease(query); + } else { + result = SecItemDelete(delete_query); + } + + CFRelease(delete_query); + CFRelease(item_ref_list); + + return result; +} + static OSStatus delete_internet_password(void) { CFDictionaryRef attrs; + CFArrayRef refs; OSStatus result; /* @@ -183,10 +229,18 @@ static OSStatus delete_internet_password(void) return -1; attrs = CREATE_SEC_ATTRIBUTES(kSecMatchLimit, kSecMatchLimitAll, + kSecReturnRef, kCFBooleanTrue, NULL); - result = SecItemDelete(attrs); + result = SecItemCopyMatching(attrs, (CFTypeRef *)&refs); CFRelease(attrs); + if (!result) { + for (CFIndex i = 0; !result && i < CFArrayGetCount(refs); i++) + result = delete_ref(CFArrayGetValueAtIndex(refs, i)); + + CFRelease(refs); + } + /* We consider not found to not be an error */ if (result == errSecItemNotFound) result = errSecSuccess; From patchwork Sat Feb 17 23:34:56 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Bo Anderson X-Patchwork-Id: 13561624 Received: from mail-wm1-f54.google.com (mail-wm1-f54.google.com [209.85.128.54]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 722358005A for ; Sat, 17 Feb 2024 23:35:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.54 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1708212905; cv=none; b=IwJuHBhACMNGSFHRdQeAGw2xkaU9jN/oIcpGvX6qb12baqLz4kF0YcpB48Ojvx0zHqHKmiB1XA9jhd8ANImfG4fIIhTIrxf1TCKGBzhYmduWtmq3QGNbKrH6EWjd9gLUBwbCZQ25Ch/R00qsgTgqncA37Gz8ANvPsn1gj1Osjy0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1708212905; c=relaxed/simple; bh=4jD8pmRlv6ad2wDanogWcel3p7qGO2vS39tO6qUq8HI=; h=Message-ID:In-Reply-To:References:From:Date:Subject:Content-Type: MIME-Version:To:Cc; b=mKqo1NFie4VO5dpU1wqT42cR/FglPTH9Ylo4GWXvc6MXFwaURX3CSGXdwAVQ9KB8ZLPOKtKjhoNEWQXNhHYT2OrkcWwaShidYPRlj2rPmD+R0z3OFVgmT1Sdo3JFBmtpUF8SEsWxB668NlZeplixueI1Ge6OCPXA6xaQOqw+1HE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=DVzpgrE4; arc=none smtp.client-ip=209.85.128.54 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="DVzpgrE4" Received: by mail-wm1-f54.google.com with SMTP id 5b1f17b1804b1-4125edd1433so4020015e9.3 for ; Sat, 17 Feb 2024 15:35:03 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1708212901; x=1708817701; darn=vger.kernel.org; h=cc:to:mime-version:content-transfer-encoding:fcc:subject:date:from :references:in-reply-to:message-id:from:to:cc:subject:date :message-id:reply-to; bh=dodAeWsH547xv+IK6TnuopmC6F/C6q3+GbJxPeogXcQ=; b=DVzpgrE4W7IWwad3/chLeguwiznnEOIXyPqCrK2gaDUjUgZaPMk7y5OfTXO/o5KlM+ e5bQcuRizJFuCh+/Yo6HlEhHeBwQcgvjssXOq1MPf6EJw76eNpLVYnrwDKBh8gIEPi/S WpaIF38W6Toe3TFem1DnBnntpLb5iXvgwAfOwv7FsFS2s+HZyJS4OvMfvBABd5o32zkM o5OJ1RT2ubQ+dw5Rvc8g875eOVK6JSgtGRCEwPJ7xdqcLfkb2XZcf3TYfuURELtFTWxm wisuE8hTaf2en/E4moWJmEAjB7pLZ8PQUEojKJHyf7AcWE8SmSodG/Mkwx4hYM2p7BlM Cc7A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1708212901; x=1708817701; h=cc:to:mime-version:content-transfer-encoding:fcc:subject:date:from :references:in-reply-to:message-id:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=dodAeWsH547xv+IK6TnuopmC6F/C6q3+GbJxPeogXcQ=; b=qLdTKZtefdpZcpQankmXOD/ktiDRFIYCvYEuVjgejhH7FuVis0KWGcUy7M1mwEJkik XUQO1YewEEX2p55N/xP+V5aADiYtL0FCMn2G8qVUK2cebrI2ahXYoO8T1GKey4I6FMVx S+1mtIHYL9BbYIcxwb3HEk8IctWIyzjiRgxr95f4rK/4bwLtV6z3Na/REZi2imvZGZXJ +nkdL1muSpo2vGEOuHKVPoxuCXfIEdbHwDboInvOxelvJKzuNW2rPv2kaoS0AU0qPdda dDAnwON23b1rcWP6jWt4h1oY2HBfkIUbPx0c1vklcpjXesKFep1fdD7rXYP9Zv9RJuGQ eCDA== X-Gm-Message-State: AOJu0YzBlY13KhToMEq/osqHCYyCSqm8IJjnp/SQoPInhBWnqx18GuRd tPTvlh5x9nc0CEegFFKAwd8q8NVwi/kvDEAHG6V+md/wRT72mdIxttwsgiNr X-Google-Smtp-Source: AGHT+IEX7Y/YQNKDDnV2x8fingFPY92iAVlSsL56YdN2NFkqW3Bvn8T8v1qlzimvL6fmOrtvLInUyw== X-Received: by 2002:a05:600c:1f17:b0:412:5efc:ef59 with SMTP id bd23-20020a05600c1f1700b004125efcef59mr1026110wmb.0.1708212901521; Sat, 17 Feb 2024 15:35:01 -0800 (PST) Received: from [127.0.0.1] ([13.74.141.28]) by smtp.gmail.com with ESMTPSA id g9-20020a05600c310900b00411e5c1bcbasm6623599wmo.0.2024.02.17.15.35.00 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 17 Feb 2024 15:35:00 -0800 (PST) Message-ID: In-Reply-To: References: Date: Sat, 17 Feb 2024 23:34:56 +0000 Subject: [PATCH 4/4] osxkeychain: store new attributes Fcc: Sent Precedence: bulk X-Mailing-List: git@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 To: git@vger.kernel.org Cc: Bo Anderson , Bo Anderson From: Bo Anderson From: Bo Anderson d208bfdfef (credential: new attribute password_expiry_utc, 2023-02-18) and a5c76569e7 (credential: new attribute oauth_refresh_token, 2023-04-21) introduced new credential attributes but support was missing from git-credential-osxkeychain. Support these attributes by appending the data to the password in the keychain, separated by line breaks. Line breaks cannot appear in a git credential password so it is an appropriate separator. Fixes the remaining test failures with osxkeychain: 18 - helper (osxkeychain) gets password_expiry_utc 19 - helper (osxkeychain) overwrites when password_expiry_utc changes 21 - helper (osxkeychain) gets oauth_refresh_token Signed-off-by: Bo Anderson --- .../osxkeychain/git-credential-osxkeychain.c | 68 +++++++++++++++++-- 1 file changed, 62 insertions(+), 6 deletions(-) diff --git a/contrib/credential/osxkeychain/git-credential-osxkeychain.c b/contrib/credential/osxkeychain/git-credential-osxkeychain.c index 9e742796336..6a40917b1ef 100644 --- a/contrib/credential/osxkeychain/git-credential-osxkeychain.c +++ b/contrib/credential/osxkeychain/git-credential-osxkeychain.c @@ -6,10 +6,12 @@ #define ENCODING kCFStringEncodingUTF8 static CFStringRef protocol; /* Stores constant strings - not memory managed */ static CFStringRef host; +static CFNumberRef port; static CFStringRef path; static CFStringRef username; static CFDataRef password; -static CFNumberRef port; +static CFDataRef password_expiry_utc; +static CFDataRef oauth_refresh_token; static void clear_credential(void) { @@ -17,6 +19,10 @@ static void clear_credential(void) CFRelease(host); host = NULL; } + if (port) { + CFRelease(port); + port = NULL; + } if (path) { CFRelease(path); path = NULL; @@ -29,12 +35,18 @@ static void clear_credential(void) CFRelease(password); password = NULL; } - if (port) { - CFRelease(port); - port = NULL; + if (password_expiry_utc) { + CFRelease(password_expiry_utc); + password_expiry_utc = NULL; + } + if (oauth_refresh_token) { + CFRelease(oauth_refresh_token); + oauth_refresh_token = NULL; } } +#define STRING_WITH_LENGTH(s) s, sizeof(s) - 1 + __attribute__((format (printf, 1, 2), __noreturn__)) static void die(const char *err, ...) { @@ -197,9 +209,27 @@ static OSStatus delete_ref(const void *itemRef) CFDictionarySetValue(query, kSecReturnData, kCFBooleanTrue); result = SecItemCopyMatching(query, (CFTypeRef *)&data); if (!result) { - if (CFEqual(data, password)) + CFDataRef kc_password; + const UInt8 *raw_data; + const UInt8 *line; + + /* Don't match appended metadata */ + raw_data = CFDataGetBytePtr(data); + line = memchr(raw_data, '\n', CFDataGetLength(data)); + if (line) + kc_password = CFDataCreateWithBytesNoCopy( + kCFAllocatorDefault, + raw_data, + line - raw_data, + kCFAllocatorNull); + else + kc_password = data; + + if (CFEqual(kc_password, password)) result = SecItemDelete(delete_query); + if (line) + CFRelease(kc_password); CFRelease(data); } @@ -250,6 +280,7 @@ static OSStatus delete_internet_password(void) static OSStatus add_internet_password(void) { + CFMutableDataRef data; CFDictionaryRef attrs; OSStatus result; @@ -257,7 +288,23 @@ static OSStatus add_internet_password(void) if (!protocol || !host || !username || !password) return -1; - attrs = CREATE_SEC_ATTRIBUTES(kSecValueData, password, + data = CFDataCreateMutableCopy(kCFAllocatorDefault, 0, password); + if (password_expiry_utc) { + CFDataAppendBytes(data, + (const UInt8 *)STRING_WITH_LENGTH("\npassword_expiry_utc=")); + CFDataAppendBytes(data, + CFDataGetBytePtr(password_expiry_utc), + CFDataGetLength(password_expiry_utc)); + } + if (oauth_refresh_token) { + CFDataAppendBytes(data, + (const UInt8 *)STRING_WITH_LENGTH("\noauth_refresh_token=")); + CFDataAppendBytes(data, + CFDataGetBytePtr(oauth_refresh_token), + CFDataGetLength(oauth_refresh_token)); + } + + attrs = CREATE_SEC_ATTRIBUTES(kSecValueData, data, NULL); result = SecItemAdd(attrs, NULL); @@ -268,6 +315,7 @@ static OSStatus add_internet_password(void) CFRelease(query); } + CFRelease(data); CFRelease(attrs); return result; @@ -339,6 +387,14 @@ static void read_credential(void) password = CFDataCreate(kCFAllocatorDefault, (UInt8 *)v, strlen(v)); + else if (!strcmp(buf, "password_expiry_utc")) + password_expiry_utc = CFDataCreate(kCFAllocatorDefault, + (UInt8 *)v, + strlen(v)); + else if (!strcmp(buf, "oauth_refresh_token")) + oauth_refresh_token = CFDataCreate(kCFAllocatorDefault, + (UInt8 *)v, + strlen(v)); /* * Ignore other lines; we don't know what they mean, but * this future-proofs us when later versions of git do