Ticket #27250: apple-keychain.patch

File apple-keychain.patch, 60.7 KB (added by lassi.tuura@…, 13 years ago)

Patch to add apple keychain support (+ other apple changes)

Line 
1--- a/Makefile.in       2008-11-05 06:20:46.000000000 +0100
2+++ b/Makefile.in       2009-02-26 01:14:06.000000000 +0100
3@@ -56,6 +56,7 @@
4 ENT=@ENT@
5 XAUTH_PATH=@XAUTH_PATH@
6 LDFLAGS=-L. -Lopenbsd-compat/ @LDFLAGS@
7+KEYCHAIN_LDFLAGS=@KEYCHAIN_LDFLAGS@
8 EXEEXT=@EXEEXT@
9 MANFMT=@MANFMT@
10 
11@@ -93,6 +94,8 @@
12        roaming_common.o roaming_serv.o \
13        sandbox-null.o sandbox-rlimit.o sandbox-systrace.o sandbox-darwin.o
14 
15+KEYCHAINOBJS=keychain.o
16+
17 MANPAGES       = moduli.5.out scp.1.out ssh-add.1.out ssh-agent.1.out ssh-keygen.1.out ssh-keyscan.1.out ssh.1.out sshd.8.out sftp-server.8.out sftp.1.out ssh-keysign.8.out ssh-pkcs11-helper.8.out sshd_config.5.out ssh_config.5.out
18 MANPAGES_IN    = moduli.5 scp.1 ssh-add.1 ssh-agent.1 ssh-keygen.1 ssh-keyscan.1 ssh.1 sshd.8 sftp-server.8 sftp.1 ssh-keysign.8 ssh-pkcs11-helper.8 sshd_config.5 ssh_config.5
19 MANTYPE                = @MANTYPE@
20@@ -125,6 +128,7 @@
21 $(LIBSSH_OBJS): Makefile.in config.h
22 $(SSHOBJS): Makefile.in config.h
23 $(SSHDOBJS): Makefile.in config.h
24+$(KEYCHAINOBJS): Makefile.in config.h
25 
26 .c.o:
27        $(CC) $(CFLAGS) $(CPPFLAGS) -c $<
28@@ -138,8 +142,8 @@
29        $(AR) rv $@ $(LIBSSH_OBJS)
30        $(RANLIB) $@
31 
32-ssh$(EXEEXT): $(LIBCOMPAT) libssh.a $(SSHOBJS)
33-       $(LD) -o $@ $(SSHOBJS) $(LDFLAGS) -lssh -lopenbsd-compat $(SSHLIBS) $(LIBS)
34+ssh$(EXEEXT): $(LIBCOMPAT) libssh.a $(SSHOBJS) $(KEYCHAINOBJS)
35+       $(LD) -o $@ $(SSHOBJS) $(KEYCHAINOBJS) $(LDFLAGS) $(KEYCHAIN_LDFLAGS) -lssh -lopenbsd-compat $(SSHLIBS) $(LIBS)
36 
37 sshd$(EXEEXT): libssh.a        $(LIBCOMPAT) $(SSHDOBJS)
38        $(LD) -o $@ $(SSHDOBJS) $(LDFLAGS) -lssh -lopenbsd-compat $(SSHDLIBS) $(LIBS)
39@@ -147,11 +151,11 @@
40 scp$(EXEEXT): $(LIBCOMPAT) libssh.a scp.o progressmeter.o
41        $(LD) -o $@ scp.o progressmeter.o bufaux.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS)
42 
43-ssh-add$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-add.o
44-       $(LD) -o $@ ssh-add.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS)
45+ssh-add$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-add.o $(KEYCHAINOBJS)
46+       $(LD) -o $@ ssh-add.o $(KEYCHAINOBJS) $(LDFLAGS) $(KEYCHAIN_LDFLAGS) -lssh -lopenbsd-compat $(LIBS)
47 
48-ssh-agent$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-agent.o ssh-pkcs11-client.o
49-       $(LD) -o $@ ssh-agent.o ssh-pkcs11-client.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS)
50+ssh-agent$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-agent.o ssh-pkcs11-client.o $(KEYCHAINOBJS)
51+       $(LD) -o $@ ssh-agent.o ssh-pkcs11-client.o $(KEYCHAINOBJS) $(LDFLAGS) $(KEYCHAIN_LDFLAGS) -lssh -lopenbsd-compat $(LIBS)
52 
53 ssh-keygen$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-keygen.o
54        $(LD) -o $@ ssh-keygen.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS)
55@@ -255,7 +259,7 @@
56        $(INSTALL) -m 0755 $(STRIP_OPT) ssh-keygen$(EXEEXT) $(DESTDIR)$(bindir)/ssh-keygen$(EXEEXT)
57        $(INSTALL) -m 0755 $(STRIP_OPT) ssh-keyscan$(EXEEXT) $(DESTDIR)$(bindir)/ssh-keyscan$(EXEEXT)
58        $(INSTALL) -m 0755 $(STRIP_OPT) sshd$(EXEEXT) $(DESTDIR)$(sbindir)/sshd$(EXEEXT)
59-       $(INSTALL) -m 4711 $(STRIP_OPT) ssh-keysign$(EXEEXT) $(DESTDIR)$(SSH_KEYSIGN)$(EXEEXT)
60+       $(INSTALL) -m 0711 $(STRIP_OPT) ssh-keysign$(EXEEXT) $(DESTDIR)$(SSH_KEYSIGN)$(EXEEXT)
61        $(INSTALL) -m 0755 $(STRIP_OPT) ssh-pkcs11-helper$(EXEEXT) $(DESTDIR)$(SSH_PKCS11_HELPER)$(EXEEXT)
62        $(INSTALL) -m 0755 $(STRIP_OPT) sftp$(EXEEXT) $(DESTDIR)$(bindir)/sftp$(EXEEXT)
63        $(INSTALL) -m 0755 $(STRIP_OPT) sftp-server$(EXEEXT) $(DESTDIR)$(SFTP_SERVER)$(EXEEXT)
64--- a/audit-bsm.c       2008-02-25 11:05:04.000000000 +0100
65+++ b/audit-bsm.c       2008-10-09 01:55:13.000000000 +0200
66@@ -194,7 +194,12 @@
67        pid_t           pid = getpid();
68        AuditInfoTermID tid = ssh_bsm_tid;
69 
70-       if (the_authctxt != NULL && the_authctxt->valid) {
71+       if (the_authctxt == NULL) {
72+               error("BSM audit: audit record internal error (NULL ctxt)");
73+               abort();
74+       }
75+       
76+       if (the_authctxt->valid) {
77                uid = the_authctxt->pw->pw_uid;
78                gid = the_authctxt->pw->pw_gid;
79        }
80--- a/auth-pam.c        2008-03-11 12:58:25.000000000 +0100
81+++ b/auth-pam.c        2008-09-26 18:10:49.000000000 +0200
82@@ -793,10 +793,11 @@
83                                xfree(msg);
84                                return (0);
85                        }
86-                       error("PAM: %s for %s%.100s from %.100s", msg,
87+                       error("PAM: %s for %s%.100s from %.100s via %s", msg,
88                            sshpam_authctxt->valid ? "" : "illegal user ",
89                            sshpam_authctxt->user,
90-                           get_remote_name_or_ip(utmp_len, options.use_dns));
91+                           get_remote_name_or_ip(utmp_len, options.use_dns),
92+                           get_local_ipaddr(packet_get_connection_in()));
93                        /* FALLTHROUGH */
94                default:
95                        *num = 0;
96--- a/authfd.c  2006-09-01 07:38:36.000000000 +0200
97+++ b/authfd.c  2008-04-18 00:15:22.000000000 +0200
98@@ -689,6 +689,29 @@
99        return decode_reply(type);
100 }
101 
102+/*
103+ * Adds identities using passphrases stored in the keychain.  This call is not
104+ * meant to be used by normal applications.
105+ */
106+
107+int
108+ssh_add_from_keychain(AuthenticationConnection *auth)
109+{
110+       Buffer msg;
111+       int type;
112+
113+       buffer_init(&msg);
114+       buffer_put_char(&msg, SSH_AGENTC_ADD_FROM_KEYCHAIN);
115+
116+       if (ssh_request_reply(auth, &msg, &msg) == 0) {
117+               buffer_free(&msg);
118+               return 0;
119+       }
120+       type = buffer_get_char(&msg);
121+       buffer_free(&msg);
122+       return decode_reply(type);
123+}
124+
125 int
126 decode_reply(int type)
127 {
128--- a/authfd.h  2006-08-05 04:39:39.000000000 +0200
129+++ b/authfd.h  2008-04-18 00:15:22.000000000 +0200
130@@ -49,6 +49,9 @@
131 #define SSH2_AGENTC_ADD_ID_CONSTRAINED         25
132 #define SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED 26
133 
134+/* keychain */
135+#define SSH_AGENTC_ADD_FROM_KEYCHAIN           27
136+
137 #define        SSH_AGENT_CONSTRAIN_LIFETIME            1
138 #define        SSH_AGENT_CONSTRAIN_CONFIRM             2
139 
140--- a/auth.c    2011-05-29 13:40:42.000000000 +0200
141+++ b/auth.c    2011-09-20 20:54:26.000000000 +0200
142@@ -209,7 +209,7 @@
143        }
144        if (options.num_deny_groups > 0 || options.num_allow_groups > 0) {
145                /* Get the user's group access list (primary and supplementary) */
146-               if (ga_init(pw->pw_name, pw->pw_gid) == 0) {
147+               if (ga_init(pw) == 0) {
148                        logit("User %.100s from %.100s not allowed because "
149                            "not in any group", pw->pw_name, hostname);
150                        return 0;
151--- a/config.h.in       2009-02-23 01:18:12.000000000 +0100
152+++ b/config.h.in       2009-02-26 01:14:06.000000000 +0100
153@@ -71,6 +70,18 @@
154 /* Define if your snprintf is busted */
155 #undef BROKEN_SNPRINTF
156 
157+/* platform uses an in-memory credentials cache */
158+#undef USE_CCAPI
159+
160+/* platform has a Security Authorization Session API */
161+#undef USE_SECURITY_SESSION_API
162+
163+/* Define to 1 if you have the `copyfile' function. */
164+#undef HAVE_COPYFILE
165+
166+/* Define to 1 if you have the <copyfile.h> header file. */
167+#undef HAVE_COPYFILE_H
168+
169 /* tcgetattr with ICANON may hang */
170 #undef BROKEN_TCGETATTR_ICANON
171 
172--- a/configure.ac      2009-02-16 05:37:03.000000000 +0100
173+++ b/configure.ac      2009-02-26 01:14:06.000000000 +0100
174@@ -4197,10 +4221,40 @@
175        AC_MSG_WARN([Please check and edit blibpath in LDFLAGS in Makefile])
176 fi
177 
178+dnl Keychain support
179+AC_ARG_WITH(keychain,
180+       [  --with-keychain=apple   Use Mac OS X Keychain],
181+       [
182+               case "$withval" in
183+               apple|no)
184+                       KEYCHAIN=$withval
185+                       ;;
186+               *)
187+                       AC_MSG_ERROR(invalid keychain type: $withval)
188+                       ;;
189+               esac
190+       ]
191+)
192+if test ! -z "$KEYCHAIN" -a "$KEYCHAIN" != "no"; then
193+       case "$KEYCHAIN" in
194+       apple)
195+               AC_CHECK_HEADERS(Security/Security.h, [
196+                               CPPFLAGS="$CPPFLAGS -D__APPLE_KEYCHAIN__"
197+                               KEYCHAIN_LDFLAGS="-framework Security -framework CoreFoundation"
198+                               AC_SUBST(KEYCHAIN_LDFLAGS)
199+                               ],
200+                               AC_MSG_WARN([Security framework not found. Disabling Mac OS X Keychain support.]))
201+               ;;
202+       esac
203+fi
204+
205 dnl Adding -Werror to CFLAGS early prevents configure tests from running.
206 dnl Add now.
207 CFLAGS="$CFLAGS $werror_flags"
208 
209+AC_CHECK_FUNCS(copyfile)
210+AC_CHECK_HEADERS(copyfile.h)
211+
212 if test "x$ac_cv_func_getaddrinfo" != "xyes" ; then
213        TEST_SSH_IPV6=no
214 else
215--- a/groupaccess.c     2008-07-04 05:51:12.000000000 +0200
216+++ b/groupaccess.c     2008-08-23 00:38:21.000000000 +0200
217@@ -33,38 +33,67 @@
218 #include <stdarg.h>
219 #include <string.h>
220 
221+#ifdef __APPLE_MEMBERSHIP__
222+#include <membership.h>
223+#endif
224+
225 #include "xmalloc.h"
226 #include "groupaccess.h"
227 #include "match.h"
228 #include "log.h"
229 
230+#ifdef __APPLE_MEMBERSHIP__
231+// SPI for 5235093
232+int32_t getgrouplist_2(const char *, gid_t, gid_t **);
233+int32_t getgroupcount(const char *, gid_t);
234+#endif
235+
236 static int ngroups;
237 static char **groups_byname;
238+#ifdef __APPLE_MEMBERSHIP__
239+uuid_t u_uuid;
240+#endif
241 
242 /*
243  * Initialize group access list for user with primary (base) and
244  * supplementary groups.  Return the number of groups in the list.
245  */
246 int
247-ga_init(const char *user, gid_t base)
248+ga_init(struct passwd *pw)
249 {
250-       gid_t *groups_bygid;
251+       gid_t *groups_bygid = NULL;
252        int i, j;
253        struct group *gr;
254 
255+#ifdef __APPLE_MEMBERSHIP__
256+       if (0 != mbr_uid_to_uuid(pw->pw_uid, u_uuid))
257+               return 0;
258+#endif
259+
260        if (ngroups > 0)
261                ga_free();
262 
263+#ifndef __APPLE_MEMBERSHIP__
264        ngroups = NGROUPS_MAX;
265 #if defined(HAVE_SYSCONF) && defined(_SC_NGROUPS_MAX)
266        ngroups = MAX(NGROUPS_MAX, sysconf(_SC_NGROUPS_MAX));
267-#endif
268-
269+#endif
270        groups_bygid = xcalloc(ngroups, sizeof(*groups_bygid));
271+#else
272+       if (-1 == (ngroups = getgrouplist_2(pw->pw_name, pw->pw_gid,
273+           &groups_bygid))) {
274+               logit("getgrouplist_2 failed");
275+               return;
276+       }
277+#endif
278        groups_byname = xcalloc(ngroups, sizeof(*groups_byname));
279-
280-       if (getgrouplist(user, base, groups_bygid, &ngroups) == -1)
281-               logit("getgrouplist: groups list too small");
282+#ifndef __APPLE_MEMBERSHIP__
283+       if (getgrouplist(pw->pw_name, pw->pw_gid, groups_bygid, &ngroups) == -1) {
284+           logit("getgrouplist: groups list too small");
285+               xfree(groups_bygid);
286+               return;
287+       }
288+#endif
289        for (i = 0, j = 0; i < ngroups; i++)
290                if ((gr = getgrgid(groups_bygid[i])) != NULL)
291                        groups_byname[j++] = xstrdup(gr->gr_name);
292@@ -75,16 +104,32 @@
293 /*
294  * Return 1 if one of user's groups is contained in groups.
295  * Return 0 otherwise.  Use match_pattern() for string comparison.
296+ * Use mbr_check_membership() for membership checking on Mac OS X.
297  */
298 int
299 ga_match(char * const *groups, int n)
300 {
301+#ifdef __APPLE_MEMBERSHIP__
302+       int i, ismember = 0;
303+       uuid_t g_uuid;
304+       struct group *grp;
305+
306+       for (i = 0; i < n; i++) {
307+               if ((grp = getgrnam(groups[i])) == NULL ||
308+                  (mbr_gid_to_uuid(grp->gr_gid, g_uuid) != 0) ||
309+                  (mbr_check_membership(u_uuid, g_uuid, &ismember) != 0))
310+                       return 0;
311+               if (ismember)
312+                       return 1;
313+       }
314+#else
315        int i, j;
316 
317        for (i = 0; i < ngroups; i++)
318                for (j = 0; j < n; j++)
319                        if (match_pattern(groups_byname[i], groups[j]))
320                                return 1;
321+#endif
322        return 0;
323 }
324 
325--- a/groupaccess.h     2008-07-04 05:51:12.000000000 +0200
326+++ b/groupaccess.h     2008-07-29 23:58:20.000000000 +0200
327@@ -27,7 +27,7 @@
328 #ifndef GROUPACCESS_H
329 #define GROUPACCESS_H
330 
331-int     ga_init(const char *, gid_t);
332+int     ga_init(struct passwd *);
333 int     ga_match(char * const *, int);
334 int     ga_match_pattern_list(const char *);
335 void    ga_free(void);
336--- a/keychain.c        1970-01-01 01:00:00.000000000 +0100
337+++ b/keychain.c        2008-11-15 00:16:08.000000000 +0100
338@@ -0,0 +1,694 @@
339+/*
340+ * Copyright (c) 2007 Apple Inc. All rights reserved.
341+ *
342+ * @APPLE_BSD_LICENSE_HEADER_START@
343+ *
344+ * Redistribution and use in source and binary forms, with or without
345+ * modification, are permitted provided that the following conditions
346+ * are met:
347+ *
348+ * 1.  Redistributions of source code must retain the above copyright
349+ *     notice, this list of conditions and the following disclaimer.
350+ * 2.  Redistributions in binary form must reproduce the above copyright
351+ *     notice, this list of conditions and the following disclaimer in the
352+ *     documentation and/or other materials provided with the distribution.
353+ * 3.  Neither the name of Apple Inc. ("Apple") nor the names of its
354+ *     contributors may be used to endorse or promote products derived from
355+ *     this software without specific prior written permission.
356+ *
357+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
358+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
359+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
360+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
361+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
362+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
363+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
364+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
365+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
366+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
367+ *
368+ * @APPLE_BSD_LICENSE_HEADER_END@
369+ */
370+
371+#include "includes.h"
372+
373+#include <stdio.h>
374+#include <string.h>
375+
376+#include "xmalloc.h"
377+#include "key.h"
378+#include "authfd.h"
379+#include "authfile.h"
380+
381+#if defined(__APPLE_KEYCHAIN__)
382+
383+#include <CoreFoundation/CoreFoundation.h>
384+#include <Security/Security.h>
385+
386+/* Our Security/SecPassword.h is not yet API, so I will define the constants that I am using here. */
387+int kSecPasswordGet     = 1<<0;  // Get password from keychain or user
388+int kSecPasswordSet     = 1<<1;  // Set password (passed in if kSecPasswordGet not set, otherwise from user)
389+int kSecPasswordFail    = 1<<2;  // Wrong password (ignore item in keychain and flag error)
390+OSStatus SecGenericPasswordCreate(SecKeychainAttributeList *searchAttrList, SecKeychainAttributeList *itemAttrList, SecPasswordRef *itemRef);
391+OSStatus SecPasswordAction(SecPasswordRef itemRef, CFTypeRef message, UInt32 flags, UInt32 *length, const void **data);
392+OSStatus SecPasswordSetInitialAccess(SecPasswordRef itemRef, SecAccessRef accessRef);
393+
394+#endif
395+
396+/*
397+ * Platform-specific helper functions.
398+ */
399+
400+#if defined(__APPLE_KEYCHAIN__)
401+
402+static int get_boolean_preference(const char *key, int default_value,
403+    int foreground)
404+{
405+       int value = default_value;
406+       CFStringRef keyRef = NULL;
407+       CFPropertyListRef valueRef = NULL;
408+
409+       keyRef = CFStringCreateWithCString(NULL, key, kCFStringEncodingUTF8);
410+       if (keyRef != NULL)
411+               valueRef = CFPreferencesCopyAppValue(keyRef,
412+                   CFSTR("org.openbsd.openssh"));
413+       if (valueRef != NULL)
414+               if (CFGetTypeID(valueRef) == CFBooleanGetTypeID())
415+                       value = CFBooleanGetValue(valueRef);
416+               else if (foreground)
417+                       fprintf(stderr, "Ignoring nonboolean %s preference.\n", key);
418+
419+       if (keyRef)
420+               CFRelease(keyRef);
421+       if (valueRef)
422+               CFRelease(valueRef);
423+
424+       return value;
425+}
426+
427+#endif
428+
429+/*
430+ * Store the passphrase for a given identity in the keychain.
431+ */
432+void
433+store_in_keychain(const char *filename, const char *passphrase)
434+{
435+
436+#if defined(__APPLE_KEYCHAIN__)
437+
438+       /*
439+        * store_in_keychain
440+        * Mac OS X implementation
441+        */
442+
443+       CFStringRef cfstr_relative_filename = NULL;
444+       CFURLRef cfurl_relative_filename = NULL, cfurl_filename = NULL;
445+       CFStringRef cfstr_filename = NULL;
446+       CFDataRef cfdata_filename = NULL;
447+       CFIndex filename_len;
448+       UInt8 *label = NULL;
449+       UInt8 *utf8_filename;
450+       OSStatus rv;
451+       SecKeychainItemRef itemRef = NULL;
452+       SecTrustedApplicationRef apps[] = {NULL, NULL, NULL};
453+       CFArrayRef trustedlist = NULL;
454+       SecAccessRef initialAccess = NULL;
455+
456+       /* Bail out if KeychainIntegration preference is -bool NO */
457+       if (get_boolean_preference("KeychainIntegration", 1, 1) == 0) {
458+               fprintf(stderr, "Keychain integration is disabled.\n");
459+               goto err;
460+       }
461+
462+       /* Interpret filename with the correct encoding. */
463+       if ((cfstr_relative_filename =
464+           CFStringCreateWithFileSystemRepresentation(NULL, filename)) == NULL)
465+           {
466+               fprintf(stderr, "CFStringCreateWithFileSystemRepresentation failed\n");
467+               goto err;
468+       }
469+       if ((cfurl_relative_filename = CFURLCreateWithFileSystemPath(NULL,
470+           cfstr_relative_filename, kCFURLPOSIXPathStyle, false)) == NULL) {
471+               fprintf(stderr, "CFURLCreateWithFileSystemPath failed\n");
472+               goto err;
473+       }
474+       if ((cfurl_filename = CFURLCopyAbsoluteURL(cfurl_relative_filename)) ==
475+           NULL) {
476+               fprintf(stderr, "CFURLCopyAbsoluteURL failed\n");
477+               goto err;
478+       }
479+       if ((cfstr_filename = CFURLCopyFileSystemPath(cfurl_filename,
480+           kCFURLPOSIXPathStyle)) == NULL) {
481+               fprintf(stderr, "CFURLCopyFileSystemPath failed\n");
482+               goto err;
483+       }
484+       if ((cfdata_filename = CFStringCreateExternalRepresentation(NULL,
485+           cfstr_filename, kCFStringEncodingUTF8, 0)) == NULL) {
486+               fprintf(stderr, "CFStringCreateExternalRepresentation failed\n");
487+               goto err;
488+       }
489+       filename_len = CFDataGetLength(cfdata_filename);
490+       if ((label = xmalloc(filename_len + 5)) == NULL) {
491+               fprintf(stderr, "xmalloc failed\n");
492+               goto err;
493+       }
494+       memcpy(label, "SSH: ", 5);
495+       utf8_filename = label + 5;
496+       CFDataGetBytes(cfdata_filename, CFRangeMake(0, filename_len),
497+           utf8_filename);
498+
499+       /* Check if we already have this passphrase. */
500+       rv = SecKeychainFindGenericPassword(NULL, 3, "SSH", filename_len,
501+           (char *)utf8_filename, NULL, NULL, &itemRef);
502+       if (rv == errSecItemNotFound) {
503+               /* Add a new keychain item. */
504+               SecKeychainAttribute attrs[] = {
505+                       {kSecLabelItemAttr, filename_len + 5, label},
506+                       {kSecServiceItemAttr, 3, "SSH"},
507+                       {kSecAccountItemAttr, filename_len, utf8_filename}
508+               };
509+               SecKeychainAttributeList attrList =
510+                   {sizeof(attrs) / sizeof(attrs[0]), attrs};
511+               if (SecTrustedApplicationCreateFromPath("/usr/bin/ssh-agent",
512+                   &apps[0]) != noErr ||
513+                   SecTrustedApplicationCreateFromPath("/usr/bin/ssh-add",
514+                   &apps[1]) != noErr ||
515+                   SecTrustedApplicationCreateFromPath("/usr/bin/ssh",
516+                   &apps[2]) != noErr) {
517+                       fprintf(stderr, "SecTrustedApplicationCreateFromPath failed\n");
518+                       goto err;
519+               }
520+               if ((trustedlist = CFArrayCreate(NULL, (const void **)apps,
521+                   sizeof(apps) / sizeof(apps[0]), &kCFTypeArrayCallBacks)) ==
522+                   NULL) {
523+                       fprintf(stderr, "CFArrayCreate failed\n");
524+                       goto err;
525+               }
526+               if (SecAccessCreate(cfstr_filename, trustedlist,
527+                   &initialAccess) != noErr) {
528+                       fprintf(stderr, "SecAccessCreate failed\n");
529+                       goto err;
530+               }
531+               if (SecKeychainItemCreateFromContent(
532+                   kSecGenericPasswordItemClass, &attrList, strlen(passphrase),
533+                   passphrase, NULL, initialAccess, NULL) == noErr)
534+                       fprintf(stderr, "Passphrase stored in keychain: %s\n", filename);
535+               else
536+                       fprintf(stderr, "Could not create keychain item\n");
537+       } else if (rv == noErr) {
538+               /* Update an existing keychain item. */
539+               if (SecKeychainItemModifyAttributesAndData(itemRef, NULL,
540+                   strlen(passphrase), passphrase) == noErr)
541+                       fprintf(stderr, "Passphrase updated in keychain: %s\n", filename);
542+               else
543+                       fprintf(stderr, "Could not modify keychain item\n");
544+       } else
545+               fprintf(stderr, "Could not access keychain\n");
546+
547+err:   /* Clean up. */
548+       if (cfstr_relative_filename)
549+               CFRelease(cfstr_relative_filename);
550+       if (cfurl_relative_filename)
551+               CFRelease(cfurl_relative_filename);
552+       if (cfurl_filename)
553+               CFRelease(cfurl_filename);
554+       if (cfstr_filename)
555+               CFRelease(cfstr_filename);
556+       if (cfdata_filename)
557+               CFRelease(cfdata_filename);
558+       if (label)
559+               xfree(label);
560+       if (itemRef)
561+               CFRelease(itemRef);
562+       if (apps[0])
563+               CFRelease(apps[0]);
564+       if (apps[1])
565+               CFRelease(apps[1]);
566+       if (apps[2])
567+               CFRelease(apps[2]);
568+       if (trustedlist)
569+               CFRelease(trustedlist);
570+       if (initialAccess)
571+               CFRelease(initialAccess);
572+
573+#else
574+
575+       /*
576+        * store_in_keychain
577+        * no keychain implementation
578+        */
579+
580+       fprintf(stderr, "Keychain is not available on this system\n");
581+
582+#endif
583+
584+}
585+
586+/*
587+ * Remove the passphrase for a given identity from the keychain.
588+ */
589+void
590+remove_from_keychain(const char *filename)
591+{
592+
593+#if defined(__APPLE_KEYCHAIN__)
594+
595+       /*
596+        * remove_from_keychain
597+        * Mac OS X implementation
598+        */
599+
600+       CFStringRef cfstr_relative_filename = NULL;
601+       CFURLRef cfurl_relative_filename = NULL, cfurl_filename = NULL;
602+       CFStringRef cfstr_filename = NULL;
603+       CFDataRef cfdata_filename = NULL;
604+       CFIndex filename_len;
605+       const UInt8 *utf8_filename;
606+       OSStatus rv;
607+       SecKeychainItemRef itemRef = NULL;
608+
609+       /* Bail out if KeychainIntegration preference is -bool NO */
610+       if (get_boolean_preference("KeychainIntegration", 1, 1) == 0) {
611+               fprintf(stderr, "Keychain integration is disabled.\n");
612+               goto err;
613+       }
614+
615+       /* Interpret filename with the correct encoding. */
616+       if ((cfstr_relative_filename =
617+           CFStringCreateWithFileSystemRepresentation(NULL, filename)) == NULL)
618+           {
619+               fprintf(stderr, "CFStringCreateWithFileSystemRepresentation failed\n");
620+               goto err;
621+       }
622+       if ((cfurl_relative_filename = CFURLCreateWithFileSystemPath(NULL,
623+           cfstr_relative_filename, kCFURLPOSIXPathStyle, false)) == NULL) {
624+               fprintf(stderr, "CFURLCreateWithFileSystemPath failed\n");
625+               goto err;
626+       }
627+       if ((cfurl_filename = CFURLCopyAbsoluteURL(cfurl_relative_filename)) ==
628+           NULL) {
629+               fprintf(stderr, "CFURLCopyAbsoluteURL failed\n");
630+               goto err;
631+       }
632+       if ((cfstr_filename = CFURLCopyFileSystemPath(cfurl_filename,
633+           kCFURLPOSIXPathStyle)) == NULL) {
634+               fprintf(stderr, "CFURLCopyFileSystemPath failed\n");
635+               goto err;
636+       }
637+       if ((cfdata_filename = CFStringCreateExternalRepresentation(NULL,
638+           cfstr_filename, kCFStringEncodingUTF8, 0)) == NULL) {
639+               fprintf(stderr, "CFStringCreateExternalRepresentation failed\n");
640+               goto err;
641+       }
642+       filename_len = CFDataGetLength(cfdata_filename);
643+       utf8_filename = CFDataGetBytePtr(cfdata_filename);
644+
645+       /* Check if we already have this passphrase. */
646+       rv = SecKeychainFindGenericPassword(NULL, 3, "SSH", filename_len,
647+           (const char *)utf8_filename, NULL, NULL, &itemRef);
648+       if (rv == noErr) {
649+               /* Remove the passphrase from the keychain. */
650+               if (SecKeychainItemDelete(itemRef) == noErr)
651+                       fprintf(stderr, "Passphrase removed from keychain: %s\n", filename);
652+               else
653+                       fprintf(stderr, "Could not remove keychain item\n");
654+       } else if (rv != errSecItemNotFound)
655+               fprintf(stderr, "Could not access keychain\n");
656+
657+err:   /* Clean up. */
658+       if (cfstr_relative_filename)
659+               CFRelease(cfstr_relative_filename);
660+       if (cfurl_relative_filename)
661+               CFRelease(cfurl_relative_filename);
662+       if (cfurl_filename)
663+               CFRelease(cfurl_filename);
664+       if (cfstr_filename)
665+               CFRelease(cfstr_filename);
666+       if (cfdata_filename)
667+               CFRelease(cfdata_filename);
668+       if (itemRef)
669+               CFRelease(itemRef);
670+
671+#else
672+
673+       /*
674+        * remove_from_keychain
675+        * no keychain implementation
676+        */
677+
678+       fprintf(stderr, "Keychain is not available on this system\n");
679+
680+#endif
681+
682+}
683+
684+/*
685+ * Add identities to ssh-agent using passphrases stored in the keychain.
686+ * Returns zero on success and nonzero on failure.
687+ * add_identity is a callback into ssh-agent.  It takes a filename and a
688+ * passphrase, and attempts to add the identity to the agent.  It returns
689+ * zero on success and nonzero on failure.
690+ */
691+int
692+add_identities_using_keychain(int (*add_identity)(const char *, const char *))
693+{
694+
695+#if defined(__APPLE_KEYCHAIN__)
696+
697+       /*
698+        * add_identities_using_keychain
699+        * Mac OS X implementation
700+        */
701+
702+       OSStatus rv;
703+       SecKeychainSearchRef searchRef;
704+       SecKeychainItemRef itemRef;
705+       UInt32 length;
706+       void *data;
707+       CFIndex maxsize;
708+
709+       /* Bail out if KeychainIntegration preference is -bool NO */
710+       if (get_boolean_preference("KeychainIntegration", 1, 0) == 0)
711+               return 0;
712+
713+       /* Search for SSH passphrases in the keychain */
714+       SecKeychainAttribute attrs[] = {
715+               {kSecServiceItemAttr, 3, "SSH"}
716+       };
717+       SecKeychainAttributeList attrList =
718+           {sizeof(attrs) / sizeof(attrs[0]), attrs};
719+       if ((rv = SecKeychainSearchCreateFromAttributes(NULL,
720+           kSecGenericPasswordItemClass, &attrList, &searchRef)) != noErr)
721+               return 0;
722+
723+       /* Iterate through the search results. */
724+       while ((rv = SecKeychainSearchCopyNext(searchRef, &itemRef)) == noErr) {
725+               UInt32 tag = kSecAccountItemAttr;
726+               UInt32 format = kSecFormatUnknown;
727+               SecKeychainAttributeInfo info = {1, &tag, &format};
728+               SecKeychainAttributeList *itemAttrList = NULL;
729+               CFStringRef cfstr_filename = NULL;
730+               char *filename = NULL;
731+               char *passphrase = NULL;
732+
733+               /* Retrieve filename and passphrase. */
734+               if ((rv = SecKeychainItemCopyAttributesAndData(itemRef, &info,
735+                   NULL, &itemAttrList, &length, &data)) != noErr)
736+                       goto err;
737+               if (itemAttrList->count != 1)
738+                       goto err;
739+               cfstr_filename = CFStringCreateWithBytes(NULL,
740+                   itemAttrList->attr->data, itemAttrList->attr->length,
741+                   kCFStringEncodingUTF8, true);
742+               maxsize = CFStringGetMaximumSizeOfFileSystemRepresentation(
743+                   cfstr_filename);
744+               if ((filename = xmalloc(maxsize)) == NULL)
745+                       goto err;
746+               if (CFStringGetFileSystemRepresentation(cfstr_filename,
747+                   filename, maxsize) == false)
748+                       goto err;
749+               if ((passphrase = xmalloc(length + 1)) == NULL)
750+                       goto err;
751+               memcpy(passphrase, data, length);
752+               passphrase[length] = '\0';
753+
754+               /* Add the identity. */
755+               add_identity(filename, passphrase);
756+
757+err:           /* Clean up. */
758+               if (itemRef)
759+                       CFRelease(itemRef);
760+               if (cfstr_filename)
761+                       CFRelease(cfstr_filename);
762+               if (filename)
763+                       xfree(filename);
764+               if (passphrase)
765+                       xfree(passphrase);
766+               if (itemAttrList)
767+                       SecKeychainItemFreeAttributesAndData(itemAttrList,
768+                           data);
769+       }
770+
771+       CFRelease(searchRef);
772+
773+       return 0;
774+
775+#else
776+
777+       /*
778+        * add_identities_using_keychain
779+        * no implementation
780+        */
781+
782+       return 1;
783+
784+#endif
785+
786+}
787+
788+/*
789+ * Prompt the user for a key's passphrase.  The user will be offered the option
790+ * of storing the passphrase in their keychain.  Returns the passphrase
791+ * (which the caller is responsible for xfreeing), or NULL if this function
792+ * fails or is not implemented.  If this function is not implemented, ssh will
793+ * fall back on the standard read_passphrase function, and the user will need
794+ * to use ssh-add -K to add their keys to the keychain.
795+ */
796+char *
797+keychain_read_passphrase(const char *filename, int oAskPassGUI)
798+{
799+
800+#if defined(__APPLE_KEYCHAIN__)
801+
802+       /*
803+        * keychain_read_passphrase
804+        * Mac OS X implementation
805+        */
806+
807+       CFStringRef cfstr_relative_filename = NULL;
808+       CFURLRef cfurl_relative_filename = NULL, cfurl_filename = NULL;
809+       CFStringRef cfstr_filename = NULL;
810+       CFDataRef cfdata_filename = NULL;
811+       CFIndex filename_len;
812+       UInt8 *label = NULL;
813+       UInt8 *utf8_filename;
814+       SecPasswordRef passRef = NULL;
815+       SecTrustedApplicationRef apps[] = {NULL, NULL, NULL};
816+       CFArrayRef trustedlist = NULL;
817+       SecAccessRef initialAccess = NULL;
818+       CFURLRef path = NULL;
819+       CFStringRef pathFinal = NULL;
820+       CFURLRef bundle_url = NULL;
821+       CFBundleRef bundle = NULL;
822+       CFStringRef promptTemplate = NULL, prompt = NULL;
823+       UInt32 length;
824+       const void *data;
825+       AuthenticationConnection *ac = NULL;
826+       char *result = NULL;
827+
828+       /* Bail out if KeychainIntegration preference is -bool NO */
829+       if (get_boolean_preference("KeychainIntegration", 1, 1) == 0)
830+               goto err;
831+
832+       /* Bail out if the user set AskPassGUI preference to -bool NO */
833+       if (get_boolean_preference("AskPassGUI", 1, 1) == 0 || oAskPassGUI == 0)
834+               goto err;
835+
836+       /* Bail out if we can't communicate with ssh-agent */
837+       if ((ac = ssh_get_authentication_connection()) == NULL)
838+               goto err;
839+
840+       /* Interpret filename with the correct encoding. */
841+       if ((cfstr_relative_filename =
842+           CFStringCreateWithFileSystemRepresentation(NULL, filename)) == NULL)
843+           {
844+               fprintf(stderr, "CFStringCreateWithFileSystemRepresentation failed\n");
845+               goto err;
846+       }
847+       if ((cfurl_relative_filename = CFURLCreateWithFileSystemPath(NULL,
848+           cfstr_relative_filename, kCFURLPOSIXPathStyle, false)) == NULL) {
849+               fprintf(stderr, "CFURLCreateWithFileSystemPath failed\n");
850+               goto err;
851+       }
852+       if ((cfurl_filename = CFURLCopyAbsoluteURL(cfurl_relative_filename)) ==
853+           NULL) {
854+               fprintf(stderr, "CFURLCopyAbsoluteURL failed\n");
855+               goto err;
856+       }
857+       if ((cfstr_filename = CFURLCopyFileSystemPath(cfurl_filename,
858+           kCFURLPOSIXPathStyle)) == NULL) {
859+               fprintf(stderr, "CFURLCopyFileSystemPath failed\n");
860+               goto err;
861+       }
862+       if ((cfdata_filename = CFStringCreateExternalRepresentation(NULL,
863+           cfstr_filename, kCFStringEncodingUTF8, 0)) == NULL) {
864+               fprintf(stderr, "CFStringCreateExternalRepresentation failed\n");
865+               goto err;
866+       }
867+       filename_len = CFDataGetLength(cfdata_filename);
868+       if ((label = xmalloc(filename_len + 5)) == NULL) {
869+               fprintf(stderr, "xmalloc failed\n");
870+               goto err;
871+       }
872+       memcpy(label, "SSH: ", 5);
873+       utf8_filename = label + 5;
874+       CFDataGetBytes(cfdata_filename, CFRangeMake(0, filename_len),
875+           utf8_filename);
876+
877+       /* Build a SecPasswordRef. */
878+       SecKeychainAttribute searchAttrs[] = {
879+               {kSecServiceItemAttr, 3, "SSH"},
880+               {kSecAccountItemAttr, filename_len, utf8_filename}
881+       };
882+       SecKeychainAttributeList searchAttrList =
883+           {sizeof(searchAttrs) / sizeof(searchAttrs[0]), searchAttrs};
884+       SecKeychainAttribute attrs[] = {
885+               {kSecLabelItemAttr, filename_len + 5, label},
886+               {kSecServiceItemAttr, 3, "SSH"},
887+               {kSecAccountItemAttr, filename_len, utf8_filename}
888+       };
889+       SecKeychainAttributeList attrList =
890+           {sizeof(attrs) / sizeof(attrs[0]), attrs};
891+       if (SecGenericPasswordCreate(&searchAttrList, &attrList, &passRef) !=
892+           noErr) {
893+               fprintf(stderr, "SecGenericPasswordCreate failed\n");
894+               goto err;
895+       }
896+       if (SecTrustedApplicationCreateFromPath("/usr/bin/ssh-agent", &apps[0])
897+           != noErr ||
898+           SecTrustedApplicationCreateFromPath("/usr/bin/ssh-add", &apps[1])
899+           != noErr ||
900+           SecTrustedApplicationCreateFromPath("/usr/bin/ssh", &apps[2])
901+           != noErr) {
902+               fprintf(stderr, "SecTrustedApplicationCreateFromPath failed\n");
903+               goto err;
904+       }
905+       if ((trustedlist = CFArrayCreate(NULL, (const void **)apps,
906+           sizeof(apps) / sizeof(apps[0]), &kCFTypeArrayCallBacks)) == NULL) {
907+               fprintf(stderr, "CFArrayCreate failed\n");
908+               goto err;
909+       }
910+       if (SecAccessCreate(cfstr_filename, trustedlist, &initialAccess)
911+           != noErr) {
912+               fprintf(stderr, "SecAccessCreate failed\n");
913+               goto err;
914+       }
915+       if (SecPasswordSetInitialAccess(passRef, initialAccess) != noErr) {
916+               fprintf(stderr, "SecPasswordSetInitialAccess failed\n");
917+               goto err;
918+       }
919+
920+       /* Request the passphrase from the user. */
921+       if ((path = CFURLCreateFromFileSystemRepresentation(NULL,
922+           (UInt8 *)filename, strlen(filename), false)) == NULL) {
923+               fprintf(stderr, "CFURLCreateFromFileSystemRepresentation failed\n");
924+               goto err;
925+       }
926+       if ((pathFinal = CFURLCopyLastPathComponent(path)) == NULL) {
927+               fprintf(stderr, "CFURLCopyLastPathComponent failed\n");
928+               goto err;
929+       }
930+       if (!((bundle_url = CFURLCreateWithFileSystemPath(NULL,
931+           CFSTR("/System/Library/CoreServices/"), kCFURLPOSIXPathStyle, true))
932+           != NULL && (bundle = CFBundleCreate(NULL, bundle_url)) != NULL &&
933+           (promptTemplate = CFCopyLocalizedStringFromTableInBundle(
934+           CFSTR("Enter your password for the SSH key \"%@\"."),
935+           CFSTR("OpenSSH"), bundle, "Text of the dialog asking the user for"
936+           "their passphrase.  The %@ will be replaced with the filename of a"
937+           "specific key.")) != NULL) &&
938+           (promptTemplate = CFStringCreateCopy(NULL,
939+           CFSTR("Enter your password for the SSH key \"%@\"."))) == NULL) {
940+               fprintf(stderr, "CFStringCreateCopy failed\n");
941+               goto err;
942+       }
943+       if ((prompt = CFStringCreateWithFormat(NULL, NULL, promptTemplate,
944+           pathFinal)) == NULL) {
945+               fprintf(stderr, "CFStringCreateWithFormat failed\n");
946+               goto err;
947+       }
948+       switch (SecPasswordAction(passRef, prompt,
949+           kSecPasswordGet|kSecPasswordFail, &length, &data)) {
950+       case noErr:
951+               result = xmalloc(length + 1);
952+               memcpy(result, data, length);
953+               result[length] = '\0';
954+
955+               /* Save password in keychain if requested. */
956+               if (noErr != SecPasswordAction(passRef, CFSTR(""), kSecPasswordSet, &length, &data))
957+                       fprintf(stderr, "Saving password to keychain failed\n");
958+
959+               /* Add password to agent. */
960+               char *comment = NULL;
961+               Key *private = key_load_private(filename, result, &comment);
962+               if (NULL == private)
963+                       break;
964+               if (ssh_add_identity_constrained(ac, private, comment, 0, 0))
965+                       fprintf(stderr, "Identity added: %s (%s)\n", filename, comment);
966+               else
967+                       fprintf(stderr, "Could not add identity: %s\n", filename);
968+               xfree(comment);
969+               key_free(private);
970+               break;
971+       case errAuthorizationCanceled:
972+               result = xmalloc(1);
973+               *result = '\0';
974+               break;
975+       default:
976+               goto err;
977+       }
978+
979+err:   /* Clean up. */
980+       if (cfstr_relative_filename)
981+               CFRelease(cfstr_relative_filename);
982+       if (cfurl_relative_filename)
983+               CFRelease(cfurl_relative_filename);
984+       if (cfurl_filename)
985+               CFRelease(cfurl_filename);
986+       if (cfstr_filename)
987+               CFRelease(cfstr_filename);
988+       if (cfdata_filename)
989+               CFRelease(cfdata_filename);
990+       if (label)
991+               xfree(label);
992+       if (passRef)
993+               CFRelease(passRef);
994+       if (apps[0])
995+               CFRelease(apps[0]);
996+       if (apps[1])
997+               CFRelease(apps[1]);
998+       if (apps[2])
999+               CFRelease(apps[2]);
1000+       if (trustedlist)
1001+               CFRelease(trustedlist);
1002+       if (initialAccess)
1003+               CFRelease(initialAccess);
1004+       if (path)
1005+               CFRelease(path);
1006+       if (pathFinal)
1007+               CFRelease(pathFinal);
1008+       if (bundle_url)
1009+               CFRelease(bundle_url);
1010+       if (bundle)
1011+               CFRelease(bundle);
1012+       if (promptTemplate)
1013+               CFRelease(promptTemplate);
1014+       if (prompt)
1015+               CFRelease(prompt);
1016+       if (ac)
1017+               ssh_close_authentication_connection(ac);
1018+
1019+       return result;
1020+
1021+#else
1022+
1023+       /*
1024+        * keychain_read_passphrase
1025+        * no implementation
1026+        */
1027+
1028+       return NULL;
1029+
1030+#endif
1031+
1032+}
1033--- a/keychain.h        1970-01-01 01:00:00.000000000 +0100
1034+++ b/keychain.h        2008-10-09 01:55:13.000000000 +0200
1035@@ -0,0 +1,45 @@
1036+/*
1037+ * Copyright (c) 2007 Apple Inc. All rights reserved.
1038+ *
1039+ * @APPLE_BSD_LICENSE_HEADER_START@
1040+ *
1041+ * Redistribution and use in source and binary forms, with or without
1042+ * modification, are permitted provided that the following conditions
1043+ * are met:
1044+ *
1045+ * 1.  Redistributions of source code must retain the above copyright
1046+ *     notice, this list of conditions and the following disclaimer.
1047+ * 2.  Redistributions in binary form must reproduce the above copyright
1048+ *     notice, this list of conditions and the following disclaimer in the
1049+ *     documentation and/or other materials provided with the distribution.
1050+ * 3.  Neither the name of Apple Inc. ("Apple") nor the names of its
1051+ *     contributors may be used to endorse or promote products derived from
1052+ *     this software without specific prior written permission.
1053+ *
1054+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
1055+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
1056+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
1057+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
1058+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
1059+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
1060+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
1061+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1062+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
1063+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1064+ *
1065+ * @APPLE_BSD_LICENSE_HEADER_END@
1066+ */
1067+
1068+/*
1069+ * KEYCHAIN indicates that keychain functionality is present.
1070+ * KEYCHAIN_* indicates the implementation to use, and implies KEYCHAIN.
1071+ */
1072+#if defined(__APPLE_KEYCHAIN__)
1073+#define KEYCHAIN
1074+#endif
1075+
1076+void    store_in_keychain(const char *filename, const char *passphrase);
1077+void    remove_from_keychain(const char *filename);
1078+int     add_identities_using_keychain(
1079+            int (*add_identity)(const char *, const char *));
1080+char   *keychain_read_passphrase(const char *filename, int oAskPassGUI);
1081--- a/readconf.c        2009-02-14 06:28:21.000000000 +0100
1082+++ b/readconf.c        2009-02-26 01:14:06.000000000 +0100
1083@@ -137,6 +137,9 @@
1084        oTunnel, oTunnelDevice, oLocalCommand, oPermitLocalCommand,
1085        oVisualHostKey, oUseRoaming, oZeroKnowledgePasswordAuthentication,
1086        oKexAlgorithms, oIPQoS, oRequestTTY,
1087+#ifdef __APPLE_KEYCHAIN__
1088+       oAskPassGUI,
1089+#endif
1090        oDeprecated, oUnsupported
1091 } OpCodes;
1092 
1093@@ -234,7 +243,9 @@
1094        { "kexalgorithms", oKexAlgorithms },
1095        { "ipqos", oIPQoS },
1096        { "requesttty", oRequestTTY },
1097-
1098+#ifdef __APPLE_KEYCHAIN__
1099+       { "askpassgui", oAskPassGUI },
1100+#endif
1101        { NULL, oBadOption }
1102 };
1103 
1104@@ -914,6 +933,12 @@
1105                        *intptr = value;
1106                break;
1107 
1108+#ifdef __APPLE_KEYCHAIN__
1109+       case oAskPassGUI:
1110+               intptr = &options->ask_pass_gui;
1111+               goto parse_flag;
1112+#endif
1113+
1114        case oDeprecated:
1115                debug("%s line %d: Deprecated option \"%s\"",
1116                    filename, linenum, keyword);
1117@@ -1065,6 +1092,9 @@
1118        options->ip_qos_interactive = -1;
1119        options->ip_qos_bulk = -1;
1120        options->request_tty = -1;
1121+#ifdef __APPLE_KEYCHAIN__
1122+       options->ask_pass_gui = -1;
1123+#endif
1124 }
1125 
1126 /*
1127@@ -1203,6 +1237,10 @@
1128                options->ip_qos_bulk = IPTOS_THROUGHPUT;
1129        if (options->request_tty == -1)
1130                options->request_tty = REQUEST_TTY_AUTO;
1131+#ifdef __APPLE_KEYCHAIN__
1132+       if (options->ask_pass_gui == -1)
1133+               options->ask_pass_gui = 1;
1134+#endif
1135        /* options->local_command should not be set by default */
1136        /* options->proxy_command should not be set by default */
1137        /* options->user will be set in the main program if appropriate */
1138--- a/readconf.h        2009-02-14 06:28:21.000000000 +0100
1139+++ b/readconf.h        2009-02-26 01:14:06.000000000 +0100
1140@@ -139,6 +139,10 @@
1141        int     use_roaming;
1142 
1143        int     request_tty;
1144+
1145+#ifdef __APPLE_KEYCHAIN__
1146+       int ask_pass_gui;
1147+#endif
1148 }       Options;
1149 
1150 #define SSHCTL_MASTER_NO       0
1151--- a/scp.1     2008-07-12 09:12:49.000000000 +0200
1152+++ b/scp.1     2008-07-28 19:29:42.000000000 +0200
1153@@ -20,7 +20,7 @@
1154 .Sh SYNOPSIS
1155 .Nm scp
1156 .Bk -words
1157-.Op Fl 12346BCpqrv
1158+.Op Fl 12346BCEpqrv
1159 .Op Fl c Ar cipher
1160 .Op Fl F Ar ssh_config
1161 .Op Fl i Ar identity_file
1162@@ -93,6 +93,8 @@
1163 flag to
1164 .Xr ssh 1
1165 to enable compression.
1166+.It Fl E
1167+Preserves extended attributes, resource forks, and ACLs.  Requires both ends to be running Mac OS X 10.4 or later.
1168 .It Fl c Ar cipher
1169 Selects the cipher to use for encrypting the data transfer.
1170 This option is directly passed to
1171--- a/scp.c     2008-11-03 09:23:45.000000000 +0100
1172+++ b/scp.c     2009-02-26 01:14:06.000000000 +0100
1173@@ -78,6 +78,9 @@
1174 #ifdef HAVE_SYS_STAT_H
1175 # include <sys/stat.h>
1176 #endif
1177+#ifdef __APPLE_XSAN__
1178+#include <sys/mount.h>
1179+#endif
1180 #ifdef HAVE_POLL_H
1181 #include <poll.h>
1182 #else
1183@@ -114,6 +117,11 @@
1184 #include "misc.h"
1185 #include "progressmeter.h"
1186 
1187+#ifdef HAVE_COPYFILE_H
1188+#include <libgen.h>
1189+#include <copyfile.h>
1190+#endif
1191+
1192 extern char *__progname;
1193 
1194 #define COPY_BUFLEN    16384
1195@@ -143,6 +151,12 @@
1196 /* This is used to store the pid of ssh_program */
1197 pid_t do_cmd_pid = -1;
1198 
1199+#ifdef HAVE_COPYFILE
1200+int copy_xattr = 0;
1201+int md_flag = 0;
1202+#endif
1203+
1204+
1205 static void
1206 killchild(int signo)
1207 {
1208@@ -323,7 +337,11 @@
1209        addargs(&args, "-oClearAllForwardings yes");
1210 
1211        fflag = tflag = 0;
1212+#if HAVE_COPYFILE
1213+       while ((ch = getopt(argc, argv, "dfl:prtvBCEc:i:P:q12346S:o:F:")) != -1)
1214+#else
1215        while ((ch = getopt(argc, argv, "dfl:prtvBCc:i:P:q12346S:o:F:")) != -1)
1216+#endif
1217                switch (ch) {
1218                /* User-visible flags. */
1219                case '1':
1220@@ -369,6 +387,11 @@
1221                        showprogress = 0;
1222                        break;
1223 
1224+#ifdef HAVE_COPYFILE
1225+               case 'E':
1226+                       copy_xattr = 1;
1227+                       break;
1228+#endif
1229                /* Server options. */
1230                case 'd':
1231                        targetshouldbedirectory = 1;
1232@@ -418,7 +441,12 @@
1233        remin = remout = -1;
1234        do_cmd_pid = -1;
1235        /* Command to be executed on remote system using "ssh". */
1236+#if HAVE_COPYFILE
1237+       (void) snprintf(cmd, sizeof cmd, "scp%s%s%s%s%s",
1238+           copy_xattr ? " -E" : "",
1239+#else
1240        (void) snprintf(cmd, sizeof cmd, "scp%s%s%s%s",
1241+#endif
1242            verbose_mode ? " -v" : "",
1243            iamrecursive ? " -r" : "", pflag ? " -p" : "",
1244            targetshouldbedirectory ? " -d" : "");
1245@@ -634,6 +662,10 @@
1246        int fd = -1, haderr, indx;
1247        char *last, *name, buf[2048], encname[MAXPATHLEN];
1248        int len;
1249+#if HAVE_COPYFILE
1250+       char md_name[MAXPATHLEN];
1251+       char *md_tmp;
1252+#endif
1253 
1254        for (indx = 0; indx < argc; ++indx) {
1255                name = argv[indx];
1256@@ -641,12 +673,26 @@
1257                len = strlen(name);
1258                while (len > 1 && name[len-1] == '/')
1259                        name[--len] = '\0';
1260+#if HAVE_COPYFILE
1261+md_next:
1262+               statbytes = 0;
1263+               if (md_flag) {
1264+                   fd = open(md_tmp, O_RDONLY, 0);
1265+                   unlink(md_tmp);
1266+                   free(md_tmp);
1267+                   if (fd < 0)
1268+                       goto syserr;
1269+               } else {
1270+#endif
1271                if ((fd = open(name, O_RDONLY|O_NONBLOCK, 0)) < 0)
1272                        goto syserr;
1273                if (strchr(name, '\n') != NULL) {
1274                        strnvis(encname, name, sizeof(encname), VIS_NL);
1275                        name = encname;
1276                }
1277+#if HAVE_COPYFILE
1278+               }
1279+#endif
1280                if (fstat(fd, &stb) < 0) {
1281 syserr:                        run_err("%s: %s", name, strerror(errno));
1282                        goto next;
1283@@ -743,6 +789,36 @@
1284                else
1285                        run_err("%s: %s", name, strerror(haderr));
1286                (void) response();
1287+#ifdef HAVE_COPYFILE
1288+               if (copy_xattr && md_flag == 0)
1289+               {
1290+                   if (!copyfile(name, NULL, 0,
1291+                           COPYFILE_ACL | COPYFILE_XATTR | COPYFILE_CHECK))
1292+                       continue;
1293+
1294+                   /*
1295+                    * this file will hold the actual metadata
1296+                    * to be transferred
1297+                    */
1298+                   md_tmp = strdup("/tmp/scp.md.XXXXXX");
1299+                   md_tmp = mktemp(md_tmp);
1300+
1301+                   if(copyfile(name, md_tmp, 0,
1302+                               COPYFILE_ACL | COPYFILE_XATTR | COPYFILE_PACK) == 0)
1303+                   {
1304+                       /*
1305+                        * this is the fake name to display
1306+                        */
1307+                       snprintf(md_name, sizeof md_name, "%s/._%s", dirname(name), basename(name));
1308+                       name = md_name;
1309+                       md_flag = 1;
1310+                       if (verbose_mode)
1311+                           fprintf(stderr, "copyfile(%s, %s, PACK)\n", name, md_tmp);
1312+                       goto md_next;
1313+                   }
1314+               } else
1315+                   md_flag = 0;
1316+#endif
1317        }
1318 }
1319 
1320@@ -891,6 +967,10 @@
1321        if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode))
1322                targisdir = 1;
1323        for (first = 1;; first = 0) {
1324+#if HAVE_COPYFILE
1325+               char md_src[MAXPATHLEN];
1326+               char md_dst[MAXPATHLEN];
1327+#endif
1328                cp = buf;
1329                if (atomicio(read, remin, cp, 1) != 1)
1330                        return;
1331@@ -1024,10 +1104,51 @@
1332                }
1333                omode = mode;
1334                mode |= S_IWRITE;
1335+
1336+#if HAVE_COPYFILE
1337+               if (copy_xattr && !strncmp(basename(curfile), "._", 2))
1338+               {
1339+                       int mdfd;
1340+                       if (targisdir)
1341+                       {
1342+                           snprintf(md_src, sizeof md_src, "%s.XXXXXX", np);
1343+                           snprintf(md_dst, sizeof md_dst, "%s/%s",
1344+                                   dirname(np), basename(np) + 2);
1345+                           if((mdfd = mkstemp(md_src)) < 0)
1346+                               continue;
1347+                       }
1348+                       else
1349+                       {
1350+                           snprintf(md_src, sizeof md_src, "%s/._%s.XXXXXX",
1351+                                   dirname(np), basename(np));
1352+                           snprintf(md_dst, sizeof md_dst, "%s", np);
1353+                           if((mdfd = mkstemp(md_src)) < 0)
1354+                               continue;
1355+                       }
1356+                       if (mdfd >= 0)
1357+                               close(mdfd);
1358+                       np = md_src;
1359+               }
1360+#endif
1361                if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) < 0) {
1362 bad:                   run_err("%s: %s", np, strerror(errno));
1363                        continue;
1364                }
1365+#ifdef __APPLE_XSAN__
1366+               {
1367+                       /*
1368+                        * Pre-allocate blocks for the destination file.
1369+                        */
1370+                       fstore_t fst;
1371+
1372+                       fst.fst_flags = 0;
1373+                       fst.fst_posmode = F_PEOFPOSMODE;
1374+                       fst.fst_offset = 0;
1375+                       fst.fst_length = size;
1376+                               
1377+                       (void) fcntl(ofd, F_PREALLOCATE, &fst);
1378+               }
1379+#endif /* __APPLE_XSAN__ */           
1380                (void) atomicio(vwrite, remout, "", 1);
1381                if ((bp = allocbuf(&buffer, ofd, COPY_BUFLEN)) == NULL) {
1382                        (void) close(ofd);
1383@@ -1111,6 +1232,29 @@
1384                        wrerrno = errno;
1385                }
1386                (void) response();
1387+#ifdef HAVE_COPYFILE
1388+               if (copy_xattr && strncmp(basename(np), "._", 2) == 0)
1389+               {
1390+                       if (verbose_mode)
1391+                           fprintf(stderr, "copyfile(%s, %s, UNPACK)\n", md_src, md_dst);
1392+                       if(!copyfile(md_src, md_dst, 0,
1393+                           COPYFILE_ACL | COPYFILE_XATTR | COPYFILE_UNPACK) < 0)
1394+                       {
1395+                           snprintf(md_dst, sizeof md_dst, "%s/._%s",
1396+                                   dirname(md_dst), basename(md_dst));
1397+                           rename(md_src, md_dst);
1398+                       } else
1399+                           unlink(md_src);
1400+                       if (setimes && wrerr == NO) {
1401+                               setimes = 0;
1402+                               if (utimes(md_dst, tv) < 0) {
1403+                                       run_err("%s: set times: %s",
1404+                                       np, strerror(errno));
1405+                                       wrerr = DISPLAYED;
1406+                               }
1407+                       }
1408+               } else
1409+#endif
1410                if (setimes && wrerr == NO) {
1411                        setimes = 0;
1412                        if (utimes(np, tv) < 0) {
1413@@ -1172,7 +1316,11 @@
1414 usage(void)
1415 {
1416        (void) fprintf(stderr,
1417+#if HAVE_COPYFILE
1418+           "usage: scp [-12346BCEpqrv] [-c cipher] [-F ssh_config] [-i identity_file]\n"
1419+#else
1420            "usage: scp [-12346BCpqrv] [-c cipher] [-F ssh_config] [-i identity_file]\n"
1421+#endif
1422            "           [-l limit] [-o ssh_option] [-P port] [-S program]\n"
1423            "           [[user@]host1:]file1 ... [[user@]host2:]file2\n");
1424        exit(1);
1425--- a/servconf.c        2008-11-03 09:23:45.000000000 +0100
1426+++ b/servconf.c        2009-02-26 01:14:06.000000000 +0100
1427@@ -135,7 +138,7 @@
1428 {
1429        /* Portable-specific options */
1430        if (options->use_pam == -1)
1431-               options->use_pam = 0;
1432+               options->use_pam = 1;
1433 
1434        /* Standard Options */
1435        if (options->protocol == SSH_PROTO_UNKNOWN)
1436@@ -213,8 +213,8 @@
1437                options->gss_strict_acceptor = 1;
1438        if (options->gss_store_rekey == -1)
1439                options->gss_store_rekey = 0;
1440        if (options->password_authentication == -1)
1441-               options->password_authentication = 1;
1442+               options->password_authentication = 0;
1443        if (options->kbd_interactive_authentication == -1)
1444                options->kbd_interactive_authentication = 0;
1445        if (options->challenge_response_authentication == -1)
1446@@ -542,7 +559,7 @@
1447        if ((pw = getpwnam(user)) == NULL) {
1448                debug("Can't match group at line %d because user %.100s does "
1449                    "not exist", line, user);
1450-       } else if (ga_init(pw->pw_name, pw->pw_gid) == 0) {
1451+       } else if (ga_init(pw) == 0) {
1452                debug("Can't Match group because user %.100s not in any group "
1453                    "at line %d", user, line);
1454        } else if (ga_match_pattern_list(grps) != 1) {
1455--- a/session.c 2009-01-28 06:29:49.000000000 +0100
1456+++ b/session.c 2009-02-26 01:14:06.000000000 +0100
1457@@ -2088,8 +2088,10 @@
1458                n_bytes = packet_remaining();
1459        tty_parse_modes(s->ttyfd, &n_bytes);
1460 
1461+#ifndef __APPLE_PRIVPTY__
1462        if (!use_privsep)
1463                pty_setowner(s->pw, s->tty);
1464+#endif
1465 
1466        /* Set window size from the packet. */
1467        pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel);
1468@@ -2327,9 +2329,11 @@
1469        if (s->pid != 0)
1470                record_logout(s->pid, s->tty, s->pw->pw_name);
1471 
1472+#ifndef __APPLE_PRIVPTY__
1473        /* Release the pseudo-tty. */
1474        if (getuid() == 0)
1475                pty_release(s->tty);
1476+#endif
1477 
1478        /*
1479         * Close the server side of the socket pairs.  We must do this after
1480--- a/ssh-add.0 2009-02-23 01:18:15.000000000 +0100
1481+++ b/ssh-add.0 2009-02-26 01:14:06.000000000 +0100
1482@@ -1,10 +1,10 @@
1483 SSH-ADD(1)                 OpenBSD Reference Manual                 SSH-ADD(1)
1484 
1485 NAME
1486-     ssh-add - adds private key identities to the authentication agent
1487+     ssh-add -- adds private key identities to the authentication agent
1488 
1489 SYNOPSIS
1490-     ssh-add [-cDdLlXx] [-t life] [file ...]
1491+     ssh-add [-cDdLlXxKk] [-t life] [file ...]
1492      ssh-add -s pkcs11
1493      ssh-add -e pkcs11
1494 
1495@@ -58,6 +58,13 @@
1496 
1497      -x      Lock the agent with a password.
1498 
1499+     -K      When adding identities, each passphrase will also be stored in
1500+             your keychain.  When removing identities with -d, each passphrase
1501+             will be removed from your keychain.
1502+
1503+     -k      Add identities to the agent using any passphrases stored in your
1504+             keychain.
1505+
1506 ENVIRONMENT
1507      DISPLAY and SSH_ASKPASS
1508              If ssh-add needs a passphrase, it will read the passphrase from
1509--- a/ssh-add.1 2007-06-12 16:00:27.000000000 +0200
1510+++ b/ssh-add.1 2008-04-18 00:15:22.000000000 +0200
1511@@ -45,7 +45,7 @@
1512 .Nd adds RSA or DSA identities to the authentication agent
1513 .Sh SYNOPSIS
1514 .Nm ssh-add
1515-.Op Fl cDdLlXx
1516+.Op Fl cDdLlXxKk
1517 .Op Fl t Ar life
1518 .Op Ar
1519 .Nm ssh-add
1520@@ -121,6 +121,12 @@
1521 Unlock the agent.
1522 .It Fl x
1523 Lock the agent with a password.
1524+.It Fl K
1525+When adding identities, each passphrase will also be stored in your
1526+keychain.  When removing identities with -d, each passphrase will be
1527+removed from your keychain.
1528+.It Fl k
1529+Add identities to the agent using any passphrases stored in your keychain.
1530 .El
1531 .Sh ENVIRONMENT
1532 .Bl -tag -width Ds
1533--- a/ssh-add.c 2008-02-28 09:13:52.000000000 +0100
1534+++ b/ssh-add.c 2008-04-18 00:15:22.000000000 +0200
1535@@ -62,6 +62,7 @@
1536 #include "authfile.h"
1537 #include "pathnames.h"
1538 #include "misc.h"
1539+#include "keychain.h"
1540 
1541 /* argv0 */
1542 extern char *__progname;
1543@@ -93,12 +94,24 @@
1544 }
1545 
1546 static int
1547-delete_file(AuthenticationConnection *ac, const char *filename)
1548+add_from_keychain(AuthenticationConnection *ac)
1549+{
1550+       if (ssh_add_from_keychain(ac) == 0)
1551+               return -1;
1552+
1553+       fprintf(stderr, "Added keychain identities.\n");
1554+       return 0;
1555+}
1556+
1557+static int
1558+delete_file(AuthenticationConnection *ac, int keychain, const char *filename)
1559 {
1560        Key *public;
1561        char *comment = NULL;
1562        int ret = -1;
1563 
1564+       if (keychain)
1565+               remove_from_keychain(filename);
1566        public = key_load_public(filename, &comment);
1567        if (public == NULL) {
1568                printf("Bad key file %s\n", filename);
1569@@ -136,7 +149,7 @@
1570 }
1571 
1572 static int
1573-add_file(AuthenticationConnection *ac, const char *filename)
1574+add_file(AuthenticationConnection *ac, int keychain, const char *filename)
1575 {
1576        Key *private;
1577        char *comment = NULL;
1578@@ -159,11 +172,16 @@
1579 
1580        /* At first, try empty passphrase */
1581        private = key_parse_private(&keyblob, filename, "", &comment);
1582+       if (keychain && private != NULL)
1583+               store_in_keychain(filename, "");
1584        if (comment == NULL)
1585                comment = xstrdup(filename);
1586        /* try last */
1587-       if (private == NULL && pass != NULL)
1588+       if (private == NULL && pass != NULL) {
1589                private = key_parse_private(&keyblob, filename, pass, NULL);
1590+               if (keychain && private != NULL)
1591+                       store_in_keychain(filename, pass);
1592+       }
1593        if (private == NULL) {
1594                /* clear passphrase since it did not work */
1595                clear_pass();
1596@@ -177,9 +195,12 @@
1597                                return -1;
1598                        }
1599                        private = key_parse_private(&keyblob, filename, pass,
1600                            &comment);
1601-                       if (private != NULL)
1602+                       if (private != NULL) {
1603+                               if (keychain)
1604+                                       store_in_keychain(filename, pass);
1605                                break;
1606+                       }
1607                        clear_pass();
1608                        snprintf(msg, sizeof msg,
1609                            "Bad passphrase, try again for %.200s: ", comment);
1610@@ -295,13 +316,13 @@
1611 }
1612 
1613 static int
1614-do_file(AuthenticationConnection *ac, int deleting, char *file)
1615+do_file(AuthenticationConnection *ac, int deleting, int keychain, char *file)
1616 {
1617        if (deleting) {
1618-               if (delete_file(ac, file) == -1)
1619+               if (delete_file(ac, keychain, file) == -1)
1620                        return -1;
1621        } else {
1622-               if (add_file(ac, file) == -1)
1623+               if (add_file(ac, keychain, file) == -1)
1624                        return -1;
1625        }
1626        return 0;
1627@@ -324,5 +345,10 @@
1628        fprintf(stderr, "  -s pkcs11   Add keys from PKCS#11 provider.\n");
1629        fprintf(stderr, "  -e pkcs11   Remove keys provided by PKCS#11 provider.\n");
1630+#ifdef KEYCHAIN
1631+       fprintf(stderr, "  -k          Add all identities stored in your keychain.\n");
1632+       fprintf(stderr, "  -K          Store passphrases in your keychain.\n");
1633+       fprintf(stderr, "              With -d, remove passphrases from your keychain.\n");
1634+#endif
1635 }
1636 
1637 int
1638@@ -334,6 +360,7 @@
1639        AuthenticationConnection *ac = NULL;
1640        char *sc_reader_id = NULL;
1641        int i, ch, deleting = 0, ret = 0;
1642+       int keychain = 0;
1643 
1644        /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
1645        sanitise_stdfd();
1646@@ -351,7 +378,7 @@
1647                    "Could not open a connection to your authentication agent.\n");
1648                exit(2);
1649        }
1650-       while ((ch = getopt(argc, argv, "lLcdDxXe:s:t:")) != -1) {
1651+       while ((ch = getopt(argc, argv, "lLcdDxXe:s:kKt:")) != -1) {
1652                switch (ch) {
1653                case 'l':
1654                case 'L':
1655@@ -373,6 +400,13 @@
1656                        if (delete_all(ac) == -1)
1657                                ret = 1;
1658                        goto done;
1659+               case 'k':
1660+                       if (add_from_keychain(ac) == -1)
1661+                               ret = 1;
1662+                       goto done;
1663+               case 'K':
1664+                       keychain = 1;
1665+                       break;
1666                case 's':
1667                        sc_reader_id = optarg;
1668                        break;
1669@@ -403,6 +437,7 @@
1670        if (argc == 0) {
1671                char buf[MAXPATHLEN];
1672                struct passwd *pw;
1673+               char *pw_dir;
1674                struct stat st;
1675                int count = 0;
1676 
1677@@ -413,21 +448,25 @@
1678                        goto done;
1679                }
1680 
1681+               pw_dir = xstrdup(pw->pw_dir);
1682+
1683                for (i = 0; default_files[i]; i++) {
1684-                       snprintf(buf, sizeof(buf), "%s/%s", pw->pw_dir,
1685+                       snprintf(buf, sizeof(buf), "%s/%s", pw_dir,
1686                            default_files[i]);
1687                        if (stat(buf, &st) < 0)
1688                                continue;
1689-                       if (do_file(ac, deleting, buf) == -1)
1690+                       if (do_file(ac, deleting, keychain, buf) == -1)
1691                                ret = 1;
1692                        else
1693                                count++;
1694                }
1695                if (count == 0)
1696                        ret = 1;
1697+
1698+               xfree(pw_dir);
1699        } else {
1700                for (i = 0; i < argc; i++) {
1701-                       if (do_file(ac, deleting, argv[i]) == -1)
1702+                       if (do_file(ac, deleting, keychain, argv[i]) == -1)
1703                                ret = 1;
1704                }
1705        }
1706--- a/ssh-agent.c       2008-07-04 15:10:49.000000000 +0200
1707+++ b/ssh-agent.c       2009-02-26 01:14:06.000000000 +0100
1708@@ -65,6 +65,9 @@
1709 #include <time.h>
1710 #include <string.h>
1711 #include <unistd.h>
1712+#ifdef __APPLE_LAUNCHD__
1713+#include <launch.h>
1714+#endif
1715 
1716 #include "xmalloc.h"
1717 #include "ssh.h"
1718@@ -72,9 +75,11 @@
1719 #include "buffer.h"
1720 #include "key.h"
1721 #include "authfd.h"
1722+#include "authfile.h"
1723 #include "compat.h"
1724 #include "log.h"
1725 #include "misc.h"
1726+#include "keychain.h"
1727 
1728 #ifdef SMARTCARD
1729 #include "scard.h"
1730@@ -714,6 +719,61 @@
1731 }
1732 #endif /* SMARTCARD */
1733 
1734+static int
1735+add_identity_callback(const char *filename, const char *passphrase)
1736+{
1737+       Key *k;
1738+       int version;
1739+       Idtab *tab;
1740+
1741+       if ((k = key_load_private(filename, passphrase, NULL)) == NULL)
1742+               return 1;
1743+       switch (k->type) {
1744+       case KEY_RSA:
1745+       case KEY_RSA1:
1746+               if (RSA_blinding_on(k->rsa, NULL) != 1) {
1747+                       key_free(k);
1748+                       return 1;
1749+               }
1750+               break;
1751+       }
1752+       version = k->type == KEY_RSA1 ? 1 : 2;
1753+       tab = idtab_lookup(version);
1754+       if (lookup_identity(k, version) == NULL) {
1755+               Identity *id = xmalloc(sizeof(Identity));
1756+               id->key = k;
1757+               id->comment = xstrdup(filename);
1758+               if (id->comment == NULL) {
1759+                       key_free(k);
1760+                       return 1;
1761+               }
1762+               id->death = 0;
1763+               id->confirm = 0;
1764+               TAILQ_INSERT_TAIL(&tab->idlist, id, next);
1765+               tab->nentries++;
1766+       } else {
1767+               key_free(k);
1768+               return 1;
1769+       }
1770+
1771+       return 0;
1772+}
1773+
1774+static void
1775+process_add_from_keychain(SocketEntry *e)
1776+{
1777+       int result;
1778+
1779+       result = add_identities_using_keychain(&add_identity_callback);
1780+
1781+       /* e will be NULL when ssh-agent adds keys on its own at startup */
1782+       if (e) {
1783+               buffer_put_int(&e->output, 1);
1784+               buffer_put_char(&e->output,
1785+                   result ? SSH_AGENT_FAILURE : SSH_AGENT_SUCCESS);
1786+       }
1787+}
1788+
1789 /* dispatch incoming messages */
1790 
1791 static void
1792@@ -806,6 +866,9 @@
1793                process_remove_smartcard_key(e);
1794                break;
1795 #endif /* ENABLE_PKCS11 */
1796+       case SSH_AGENTC_ADD_FROM_KEYCHAIN:
1797+               process_add_from_keychain(e);
1798+               break;
1799        default:
1800                /* Unknown message.  Respond with failure. */
1801                error("Unknown message %d", type);
1802@@ -1046,7 +1109,11 @@
1803 int
1804 main(int ac, char **av)
1805 {
1806+#ifdef __APPLE_LAUNCHD__
1807+       int c_flag = 0, d_flag = 0, k_flag = 0, s_flag = 0, l_flag = 0;
1808+#else
1809        int c_flag = 0, d_flag = 0, k_flag = 0, s_flag = 0;
1810+#endif
1811        int sock, fd, ch, result, saved_errno;
1812        u_int nalloc;
1813        char *shell, *format, *pidstr, *agentsocket = NULL;
1814@@ -1080,7 +1147,11 @@
1815        init_rng();
1816        seed_rng();
1817 
1818+#ifdef __APPLE_LAUNCHD__
1819+       while ((ch = getopt(ac, av, "cdklsa:t:")) != -1) {
1820+#else
1821        while ((ch = getopt(ac, av, "cdksa:t:")) != -1) {
1822+#endif
1823                switch (ch) {
1824                case 'c':
1825                        if (s_flag)
1826@@ -1090,6 +1161,11 @@
1827                case 'k':
1828                        k_flag++;
1829                        break;
1830+#ifdef __APPLE_LAUNCHD__
1831+               case 'l':
1832+                       l_flag++;
1833+                       break;
1834+#endif
1835                case 's':
1836                        if (c_flag)
1837                                usage();
1838@@ -1116,7 +1192,11 @@
1839        ac -= optind;
1840        av += optind;
1841 
1842+#ifdef __APPPLE_LAUNCHD__
1843+       if (ac > 0 && (c_flag || k_flag || s_flag || d_flag || l_flag))
1844+#else
1845        if (ac > 0 && (c_flag || k_flag || s_flag || d_flag))
1846+#endif
1847                usage();
1848 
1849        if (ac == 0 && !c_flag && !s_flag) {
1850@@ -1172,6 +1252,53 @@
1851         * Create socket early so it will exist before command gets run from
1852         * the parent.
1853         */
1854+#ifdef __APPLE_LAUNCHD__
1855+       if (l_flag) {
1856+               launch_data_t resp, msg, tmp;
1857+               size_t listeners_i;
1858+
1859+               msg = launch_data_new_string(LAUNCH_KEY_CHECKIN);
1860+
1861+               resp = launch_msg(msg);
1862+
1863+               if (NULL == resp) {
1864+                       perror("launch_msg");
1865+                       exit(1);
1866+               }
1867+               launch_data_free(msg);
1868+               switch (launch_data_get_type(resp)) {
1869+               case LAUNCH_DATA_ERRNO:
1870+                       errno = launch_data_get_errno(resp);
1871+                       perror("launch_msg response");
1872+                       exit(1);
1873+               case LAUNCH_DATA_DICTIONARY:
1874+                       break;
1875+               default:
1876+                       fprintf(stderr, "launch_msg unknown response");
1877+                       exit(1);
1878+               }
1879+               tmp = launch_data_dict_lookup(resp, LAUNCH_JOBKEY_SOCKETS);
1880+
1881+               if (NULL == tmp) {
1882+                       fprintf(stderr, "no sockets\n");
1883+                       exit(1);
1884+               }
1885+
1886+               tmp = launch_data_dict_lookup(tmp, "Listeners");
1887+
1888+               if (NULL == tmp) {
1889+                       fprintf(stderr, "no known listeners\n");
1890+                       exit(1);
1891+               }
1892+
1893+               for (listeners_i = 0; listeners_i < launch_data_array_get_count(tmp); listeners_i++) {
1894+                       launch_data_t obj_at_ind = launch_data_array_get_index(tmp, listeners_i);
1895+                       new_socket(AUTH_SOCKET, launch_data_get_fd(obj_at_ind));
1896+               }
1897+
1898+               launch_data_free(resp);
1899+       } else {
1900+#endif
1901        sock = socket(AF_UNIX, SOCK_STREAM, 0);
1902        if (sock < 0) {
1903                perror("socket");
1904@@ -1193,6 +1320,14 @@
1905                perror("listen");
1906                cleanup_exit(1);
1907        }
1908+#ifdef __APPLE_LAUNCHD__
1909+       }
1910+#endif
1911+
1912+#ifdef __APPLE_LAUNCHD__
1913+       if (l_flag)
1914+               goto skip2;
1915+#endif
1916 
1917        /*
1918         * Fork, and have the parent execute the command, if any, or present
1919@@ -1261,6 +1396,7 @@
1920 
1921 skip:
1922        new_socket(AUTH_SOCKET, sock);
1923+skip2:
1924        if (ac > 0)
1925                parent_alive_interval = 10;
1926        idtab_init();
1927@@ -1271,6 +1407,10 @@
1928        signal(SIGTERM, cleanup_handler);
1929        nalloc = 0;
1930 
1931+#ifdef KEYCHAIN
1932+       process_add_from_keychain(NULL);
1933+#endif
1934+
1935        while (1) {
1936                prepare_select(&readsetp, &writesetp, &max_fd, &nalloc, &tvp);
1937                result = select(max_fd + 1, readsetp, writesetp, NULL, tvp);
1938--- a/ssh-keysign.8     2007-06-05 10:27:13.000000000 +0200
1939+++ b/ssh-keysign.8     2008-07-28 19:29:42.000000000 +0200
1940@@ -71,6 +71,9 @@
1941 Since they are readable only by root,
1942 .Nm
1943 must be set-uid root if host-based authentication is used.
1944+Note that
1945+.Nm
1946+is not set-uid by default on Mac OS X.
1947 .Pp
1948 .It Pa /etc/ssh/ssh_host_dsa_key-cert.pub
1949 .It Pa /etc/ssh/ssh_host_ecdsa_key-cert.pub
1950--- a/sshconnect1.c     2006-11-07 13:14:42.000000000 +0100
1951+++ b/sshconnect1.c     2008-10-09 01:55:13.000000000 +0200
1952@@ -47,6 +47,7 @@
1953 #include "canohost.h"
1954 #include "hostfile.h"
1955 #include "auth.h"
1956+#include "keychain.h"
1957 
1958 /* Session id for the current session. */
1959 u_char session_id[16];
1960@@ -260,6 +261,10 @@
1961                snprintf(buf, sizeof(buf),
1962                    "Enter passphrase for RSA key '%.100s': ", comment);
1963                for (i = 0; i < options.number_of_password_prompts; i++) {
1964+#ifdef __APPLE_KEYCHAIN__
1965+                       passphrase = keychain_read_passphrase(comment, options.ask_pass_gui);
1966+                       if (passphrase == NULL)
1967+#endif
1968                        passphrase = read_passphrase(buf, 0);
1969                        if (strcmp(passphrase, "") != 0) {
1970                                private = key_load_private_type(KEY_RSA1,
1971--- a/sshconnect2.c     2008-11-05 06:20:47.000000000 +0100
1972+++ b/sshconnect2.c     2009-02-26 01:14:06.000000000 +0100
1973@@ -69,6 +69,7 @@
1974 #include "pathnames.h"
1975 #include "uidswap.h"
1976 #include "jpake.h"
1977+#include "keychain.h"
1978 
1979 #ifdef GSSAPI
1980 #include "ssh-gss.h"
1981@@ -1447,6 +1448,10 @@
1982                snprintf(prompt, sizeof prompt,
1983                    "Enter passphrase for key '%.100s': ", filename);
1984                for (i = 0; i < options.number_of_password_prompts; i++) {
1985+#ifdef __APPLE_KEYCHAIN__
1986+                       passphrase = keychain_read_passphrase(filename, options.ask_pass_gui);
1987+                       if (passphrase == NULL)
1988+#endif
1989                        passphrase = read_passphrase(prompt, 0);
1990                        if (strcmp(passphrase, "") != 0) {
1991                                private = key_load_private_type(KEY_UNSPEC,
1992--- a/sshd.0    2009-02-23 01:18:15.000000000 +0100
1993+++ b/sshd.0    2009-02-26 01:14:06.000000000 +0100
1994@@ -556,8 +556,8 @@
1995 
1996 SEE ALSO
1997      scp(1), sftp(1), ssh(1), ssh-add(1), ssh-agent(1), ssh-keygen(1),
1998-     ssh-keyscan(1), chroot(2), hosts_access(5), login.conf(5), moduli(5),
1999-     sshd_config(5), inetd(8), sftp-server(8)
2000+     ssh-keyscan(1), chroot(2), hosts_access(5), sshd_config(5)
2001+     sftp-server(8)
2002 
2003 AUTHORS
2004      OpenSSH is a derivative of the original and free ssh 1.2.12 release by
2005--- a/sshd.8    2008-11-03 09:21:21.000000000 +0100
2006+++ b/sshd.8    2009-02-26 01:14:06.000000000 +0100
2007@@ -873,10 +873,7 @@
2008 .Xr ssh-keyscan 1 ,
2009 .Xr chroot 2 ,
2010 .Xr hosts_access 5 ,
2011-.Xr login.conf 5 ,
2012-.Xr moduli 5 ,
2013 .Xr sshd_config 5 ,
2014-.Xr inetd 8 ,
2015 .Xr sftp-server 8
2016 .Sh AUTHORS
2017 OpenSSH is a derivative of the original and free
2018--- a/sshd.c    2009-01-28 06:31:23.000000000 +0100
2019+++ b/sshd.c    2009-02-26 01:14:06.000000000 +0100
2020@@ -1887,6 +1976,12 @@
2021        audit_event(SSH_AUTH_SUCCESS);
2022 #endif
2023 
2024+#ifdef USE_PAM
2025+       if (options.use_pam) {
2026+               do_pam_setcred(1);
2027+               do_pam_session();
2028+       }
2029+#endif
2030 #ifdef GSSAPI
2031        if (options.gss_authentication) {
2032                temporarily_use_uid(authctxt->pw);
2033@@ -1894,12 +1989,6 @@
2034                restore_uid();
2035        }
2036 #endif
2037-#ifdef USE_PAM
2038-       if (options.use_pam) {
2039-               do_pam_setcred(1);
2040-               do_pam_session();
2041-       }
2042-#endif
2043 
2044        /*
2045         * In privilege separation, we fork another child and prepare
2046--- a/sshd_config       2008-07-02 14:35:43.000000000 +0200
2047+++ b/sshd_config       2008-12-10 00:05:59.000000000 +0100
2048@@ -32,7 +32,7 @@
2049 
2050 # Logging
2051 # obsoletes QuietMode and FascistLogging
2052-#SyslogFacility AUTH
2053+SyslogFacility AUTHPRIV
2054 #LogLevel INFO
2055 
2056 # Authentication:
2057@@ -57,10 +57,11 @@
2058 # Don't read the user's ~/.rhosts and ~/.shosts files
2059 #IgnoreRhosts yes
2060 
2061-# To disable tunneled clear text passwords, change to no here!
2062-#PasswordAuthentication yes
2063+# To disable tunneled clear text passwords, change to no here! Also,
2064+# remember to set the UsePAM setting to 'no'.
2065+#PasswordAuthentication no
2066 #PermitEmptyPasswords no
2067 
2068 # Change to no to disable s/key passwords
2069 #ChallengeResponseAuthentication yes
2070 
2071@@ -83,7 +90,10 @@
2072 # If you just want the PAM account and session checks to run without
2073 # PAM authentication, then enable this but set PasswordAuthentication
2074 # and ChallengeResponseAuthentication to 'no'.
2075-#UsePAM no
2076+# Also, PAM will deny null passwords by default.  If you need to allow
2077+# null passwords, add the "    nullok" option to the end of the
2078+# securityserver.so line in /etc/pam.d/sshd.
2079+#UsePAM yes
2080 
2081 #AllowAgentForwarding yes
2082 #AllowTcpForwarding yes
2083--- a/sshd_config.0     2009-02-23 01:18:15.000000000 +0100
2084+++ b/sshd_config.0     2009-02-26 01:14:06.000000000 +0100
2085@@ -371,7 +365,7 @@
2086 
2087      PasswordAuthentication
2088              Specifies whether password authentication is allowed.  The
2089-             default is ``yes''.
2090+             default is ``no''.
2091 
2092      PermitEmptyPasswords
2093              When password authentication is allowed, it specifies whether the
2094@@ -535,7 +534,7 @@
2095              either PasswordAuthentication or ChallengeResponseAuthentication.
2096 
2097              If UsePAM is enabled, you will not be able to run sshd(8) as a
2098-             non-root user.  The default is ``no''.
2099+             non-root user.  The default is ``yes''.
2100 
2101      UsePrivilegeSeparation
2102              Specifies whether sshd(8) separates privileges by creating an un-
2103--- a/sshd_config.5     2009-02-23 01:00:24.000000000 +0100
2104+++ b/sshd_config.5     2009-02-26 01:14:06.000000000 +0100
2105@@ -651,7 +666,7 @@
2106 .It Cm PasswordAuthentication
2107 Specifies whether password authentication is allowed.
2108 The default is
2109-.Dq yes .
2110+.Dq no .
2111 .It Cm PermitEmptyPasswords
2112 When password authentication is allowed, it specifies whether the
2113 server allows login to accounts with empty password strings.
2114@@ -922,7 +942,7 @@
2115 .Xr sshd 8
2116 as a non-root user.
2117 The default is
2118-.Dq no .
2119+.Dq yes .
2120 .It Cm UsePrivilegeSeparation
2121 Specifies whether
2122 .Xr sshd 8