diff options
author | Marcus Alves Grando <mnag@FreeBSD.org> | 2007-03-12 22:13:18 +0000 |
---|---|---|
committer | Marcus Alves Grando <mnag@FreeBSD.org> | 2007-03-12 22:13:18 +0000 |
commit | 1b02a74de9e69f59769636dfbff656bd8b6f160b (patch) | |
tree | 8c59ce3c7ac5219b2180fbb6b905e14a5903049a /security/openssh-portable | |
parent | 344285a70302edbcda599fa17ba7cf34ae485471 (diff) | |
download | ports-1b02a74de9e69f59769636dfbff656bd8b6f160b.tar.gz ports-1b02a74de9e69f59769636dfbff656bd8b6f160b.zip |
Notes
Diffstat (limited to 'security/openssh-portable')
-rw-r--r-- | security/openssh-portable/Makefile | 13 | ||||
-rw-r--r-- | security/openssh-portable/distinfo | 18 | ||||
-rw-r--r-- | security/openssh-portable/files/openssh-4.5p1-gsskex-20061220.patch | 2337 | ||||
-rw-r--r-- | security/openssh-portable/files/openssh-lpk.patch | 1813 |
4 files changed, 2353 insertions, 1828 deletions
diff --git a/security/openssh-portable/Makefile b/security/openssh-portable/Makefile index c532b8bfe697..ced66efdfb70 100644 --- a/security/openssh-portable/Makefile +++ b/security/openssh-portable/Makefile @@ -6,7 +6,7 @@ # PORTNAME= openssh -DISTVERSION= 4.5p1 +DISTVERSION= 4.6p1 PORTEPOCH= 1 CATEGORIES= security ipv6 MASTER_SITES= ftp://ftp.openbsd.org/pub/OpenBSD/OpenSSH/portable/%SUBDIR%/ \ @@ -100,8 +100,7 @@ CONFIGURE_ARGS+= --disable-suid-ssh .if defined(KRB5_HOME) && exists(${KRB5_HOME}) || defined(WITH_GSSAPI) .if defined(WITH_KERB_GSSAPI) PATCH_DIST_STRIP= -p0 -PATCH_SITES+= http://www.sxw.org.uk/computing/patches/ -PATCHFILES+= openssh-4.4p1-gsskex-20061002.patch +EXTRA_PATCHES+= ${FILESDIR}/openssh-4.5p1-gsskex-20061220.patch .endif PORTABLE_SUFFIX= # empty GSSAPI_SUFFIX= -gssapi @@ -146,12 +145,14 @@ BROKEN= HPN and LPK patches are incompatible .if defined(WITH_HPN) PATCH_DIST_STRIP= -p1 PATCH_SITES+= http://www.psc.edu/networking/projects/hpn-ssh/ -PATCHFILES+= openssh-4.5p1-hpn12v14.diff.gz +PATCHFILES+= openssh-4.6p1-hpn12v16.diff.gz .endif +# See http://dev.inversepath.com/trac/openssh-lpk .if defined(WITH_LPK) -PATCH_DIST_STRIP= -p0 -EXTRA_PATCHES+= ${FILESDIR}/openssh-lpk.patch +PATCH_DIST_STRIP= -p1 +PATCH_SITES+= http://dev.inversepath.com/openssh-lpk/ +PATCHFILES+= openssh-lpk-4.5p1-0.3.8.patch USE_OPENLDAP= yes CPPFLAGS+= "-I${LOCALBASE}/include -DWITH_LDAP_PUBKEY" CONFIGURE_ARGS+= --with-libs='-lldap' --with-ldflags='-L/usr/local/lib' \ diff --git a/security/openssh-portable/distinfo b/security/openssh-portable/distinfo index a9f949d24d18..68cb1588d42c 100644 --- a/security/openssh-portable/distinfo +++ b/security/openssh-portable/distinfo @@ -1,9 +1,9 @@ -MD5 (openssh-4.5p1.tar.gz) = 6468c339886f78e8a149b88f695839dd -SHA256 (openssh-4.5p1.tar.gz) = 7046b9d372f9e31ca654a66492310c188470480ddab300eb715dbf5e2177ae55 -SIZE (openssh-4.5p1.tar.gz) = 965925 -MD5 (openssh-4.4p1-gsskex-20061002.patch) = 61e8573f7192eb1ff26036f486db445a -SHA256 (openssh-4.4p1-gsskex-20061002.patch) = cc21b7038589be27924bdc0ba163cf059ea1f0b7b65d0f8d7ddc015cac6d9ac4 -SIZE (openssh-4.4p1-gsskex-20061002.patch) = 69152 -MD5 (openssh-4.5p1-hpn12v14.diff.gz) = 86d3751f777c9c99663aebbb36281a0e -SHA256 (openssh-4.5p1-hpn12v14.diff.gz) = 5cc6cd882cbb94498483b44722b3e81c8e6d7854dc2b2c57e1d56040bfdc23bd -SIZE (openssh-4.5p1-hpn12v14.diff.gz) = 15791 +MD5 (openssh-4.6p1.tar.gz) = 6a7fa99f44d9e1b5b04d15256e1405bb +SHA256 (openssh-4.6p1.tar.gz) = 7bbe277faa80c8d8d9cb96111db65fc0007d451784cc459207cd46b746a6f23a +SIZE (openssh-4.6p1.tar.gz) = 967395 +MD5 (openssh-4.6p1-hpn12v16.diff.gz) = 0bc643a4e1588f7abdcdbb31304bac56 +SHA256 (openssh-4.6p1-hpn12v16.diff.gz) = f304e3b04e2772c27c09e9aa5a3fd02747187a8775de39879c3d871fad1e156f +SIZE (openssh-4.6p1-hpn12v16.diff.gz) = 15944 +MD5 (openssh-lpk-4.5p1-0.3.8.patch) = 663ce0f968c4f7eacc5128dc91645245 +SHA256 (openssh-lpk-4.5p1-0.3.8.patch) = c96cb24ea0c21cd93f2a28c31c38902bed8ddf0cb7a2879337bb21a5edd7ce35 +SIZE (openssh-lpk-4.5p1-0.3.8.patch) = 62340 diff --git a/security/openssh-portable/files/openssh-4.5p1-gsskex-20061220.patch b/security/openssh-portable/files/openssh-4.5p1-gsskex-20061220.patch new file mode 100644 index 000000000000..edec24fcf326 --- /dev/null +++ b/security/openssh-portable/files/openssh-4.5p1-gsskex-20061220.patch @@ -0,0 +1,2337 @@ +# +# http://www.sxw.org.uk/computing/patches/openssh-4.5p1-gsskex-20061220.patch +# +Index: ChangeLog.gssapi +=================================================================== +RCS file: ChangeLog.gssapi +diff -N ChangeLog.gssapi +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ ChangeLog.gssapi 20 Dec 2006 10:41:41 -0000 +@@ -0,0 +1,64 @@ ++20061220 ++ - [ servconf.c ] ++ Make default for GSSAPIStrictAcceptorCheck be Yes, to match previous, and ++ documented, behaviour. Reported by Dan Watson. ++ ++20060910 ++ - [ gss-genr.c kexgssc.c kexgsss.c kex.h monitor.c sshconnect2.c sshd.c ++ ssh-gss.h ] ++ add support for gss-group14-sha1 key exchange mechanisms ++ - [ gss-serv.c servconf.c servconf.h sshd_config sshd_config.5 ] ++ Add GSSAPIStrictAcceptorCheck option to allow the disabling of ++ acceptor principal checking on multi-homed machines. ++ <Bugzilla #928> ++ - [ sshd_config ssh_config ] ++ Add settings for GSSAPIKeyExchange and GSSAPITrustDNS to the sample ++ configuration files ++ - [ kexgss.c kegsss.c sshconnect2.c sshd.c ] ++ Code cleanup. Replace strlen/xmalloc/snprintf sequences with xasprintf() ++ Limit length of error messages displayed by client ++ ++20060909 ++ - [ gss-genr.c gss-serv.c ] ++ move ssh_gssapi_acquire_cred() and ssh_gssapi_server_ctx to be server ++ only, where they belong ++ <Bugzilla #1225> ++ ++20060829 ++ - [ gss-serv-krb5.c ] ++ Fix CCAPI credentials cache name when creating KRB5CCNAME environment ++ variable ++ ++20060828 ++ - [ gss-genr.c ] ++ Avoid Heimdal context freeing problem ++ <Fixed upstream 20060829> ++ ++20060818 ++ - [ gss-genr.c ssh-gss.h sshconnect2.c ] ++ Make sure that SPENGO is disabled ++ <Bugzilla #1218 - Fixed upstream 20060818> ++ ++20060421 ++ - [ gssgenr.c, sshconnect2.c ] ++ a few type changes (signed versus unsigned, int versus size_t) to ++ fix compiler errors/warnings ++ (from jbasney AT ncsa.uiuc.edu) ++ - [ kexgssc.c, sshconnect2.c ] ++ fix uninitialized variable warnings ++ (from jbasney AT ncsa.uiuc.edu) ++ - [ gssgenr.c ] ++ pass oid to gss_display_status (helpful when using GSSAPI mechglue) ++ (from jbasney AT ncsa.uiuc.edu) ++ <Bugzilla #1220 > ++ - [ gss-serv-krb5.c ] ++ #ifdef HAVE_GSSAPI_KRB5 should be #ifdef HAVE_GSSAPI_KRB5_H ++ (from jbasney AT ncsa.uiuc.edu) ++ <Fixed upstream 20060304> ++ - [ readconf.c, readconf.h, ssh_config.5, sshconnect2.c ++ add client-side GssapiKeyExchange option ++ (from jbasney AT ncsa.uiuc.edu) ++ - [ sshconnect2.c ] ++ add support for GssapiTrustDns option for gssapi-with-mic ++ (from jbasney AT ncsa.uiuc.edu) ++ <gssapi-with-mic support is Bugzilla #1008> +Index: Makefile.in +=================================================================== +RCS file: /cvs/openssh/Makefile.in,v +retrieving revision 1.283 +diff -u -r1.283 Makefile.in +--- Makefile.in 23 Oct 2006 21:44:47 -0000 1.283 ++++ Makefile.in 20 Dec 2006 10:41:42 -0000 +@@ -74,7 +74,7 @@ + atomicio.o key.o dispatch.o kex.o mac.o uidswap.o uuencode.o misc.o \ + monitor_fdpass.o rijndael.o ssh-dss.o ssh-rsa.o dh.o kexdh.o \ + kexgex.o kexdhc.o kexgexc.o scard.o msg.o progressmeter.o dns.o \ +- entropy.o scard-opensc.o gss-genr.o ++ entropy.o scard-opensc.o gss-genr.o kexgssc.o + + SSHOBJS= ssh.o readconf.o clientloop.o sshtty.o \ + sshconnect.o sshconnect1.o sshconnect2.o +@@ -87,7 +87,7 @@ + auth2-none.o auth2-passwd.o auth2-pubkey.o \ + monitor_mm.o monitor.o monitor_wrap.o kexdhs.o kexgexs.o \ + auth-krb5.o \ +- auth2-gss.o gss-serv.o gss-serv-krb5.o \ ++ auth2-gss.o gss-serv.o gss-serv-krb5.o kexgsss.o\ + loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o \ + audit.o audit-bsm.o platform.o + +Index: auth-krb5.c +=================================================================== +RCS file: /cvs/openssh/auth-krb5.c,v +retrieving revision 1.35 +diff -u -r1.35 auth-krb5.c +--- auth-krb5.c 5 Aug 2006 02:39:39 -0000 1.35 ++++ auth-krb5.c 20 Dec 2006 10:41:42 -0000 +@@ -166,8 +166,13 @@ + + len = strlen(authctxt->krb5_ticket_file) + 6; + authctxt->krb5_ccname = xmalloc(len); ++#ifdef USE_CCAPI ++ snprintf(authctxt->krb5_ccname, len, "API:%s", ++ authctxt->krb5_ticket_file); ++#else + snprintf(authctxt->krb5_ccname, len, "FILE:%s", + authctxt->krb5_ticket_file); ++#endif + + #ifdef USE_PAM + if (options.use_pam) +@@ -219,15 +224,22 @@ + #ifndef HEIMDAL + krb5_error_code + ssh_krb5_cc_gen(krb5_context ctx, krb5_ccache *ccache) { +- int tmpfd, ret; ++ int ret; + char ccname[40]; + mode_t old_umask; ++#ifdef USE_CCAPI ++ char cctemplate[] = "API:krb5cc_%d"; ++#else ++ char cctemplate[] = "FILE:/tmp/krb5cc_%d_XXXXXXXXXX"; ++ int tmpfd; ++#endif + + ret = snprintf(ccname, sizeof(ccname), +- "FILE:/tmp/krb5cc_%d_XXXXXXXXXX", geteuid()); ++ cctemplate, geteuid()); + if (ret < 0 || (size_t)ret >= sizeof(ccname)) + return ENOMEM; + ++#ifndef USE_CCAPI + old_umask = umask(0177); + tmpfd = mkstemp(ccname + strlen("FILE:")); + umask(old_umask); +@@ -242,6 +254,7 @@ + return errno; + } + close(tmpfd); ++#endif + + return (krb5_cc_resolve(ctx, ccname, ccache)); + } +Index: auth.h +=================================================================== +RCS file: /cvs/openssh/auth.h,v +retrieving revision 1.76 +diff -u -r1.76 auth.h +--- auth.h 18 Aug 2006 14:32:46 -0000 1.76 ++++ auth.h 20 Dec 2006 10:41:42 -0000 +@@ -53,6 +53,7 @@ + int valid; /* user exists and is allowed to login */ + int attempt; + int failures; ++ int server_caused_failure; + int force_pwchange; + char *user; /* username sent by the client */ + char *service; +Index: auth2-gss.c +=================================================================== +RCS file: /cvs/openssh/auth2-gss.c,v +retrieving revision 1.18 +diff -u -r1.18 auth2-gss.c +--- auth2-gss.c 1 Sep 2006 05:38:36 -0000 1.18 ++++ auth2-gss.c 20 Dec 2006 10:41:42 -0000 +@@ -52,6 +52,39 @@ + static void input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt); + static void input_gssapi_errtok(int, u_int32_t, void *); + ++/* ++ * The 'gssapi_keyex' userauth mechanism. ++ */ ++static int ++userauth_gsskeyex(Authctxt *authctxt) ++{ ++ int authenticated = 0; ++ Buffer b; ++ gss_buffer_desc mic, gssbuf; ++ u_int len; ++ ++ mic.value = packet_get_string(&len); ++ mic.length = len; ++ ++ packet_check_eom(); ++ ++ ssh_gssapi_buildmic(&b, authctxt->user, authctxt->service, ++ "gssapi-keyex"); ++ ++ gssbuf.value = buffer_ptr(&b); ++ gssbuf.length = buffer_len(&b); ++ ++ /* gss_kex_context is NULL with privsep, so we can't check it here */ ++ if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gss_kex_context, ++ &gssbuf, &mic)))) ++ authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user)); ++ ++ buffer_free(&b); ++ xfree(mic.value); ++ ++ return (authenticated); ++} ++ + /* + * We only support those mechanisms that we know about (ie ones that we know + * how to check local user kuserok and the like) +@@ -102,6 +135,7 @@ + + if (!present) { + xfree(doid); ++ authctxt->server_caused_failure = 1; + return (0); + } + +@@ -109,6 +143,7 @@ + if (ctxt != NULL) + ssh_gssapi_delete_ctx(&ctxt); + xfree(doid); ++ authctxt->server_caused_failure = 1; + return (0); + } + +@@ -291,6 +326,12 @@ + dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL); + userauth_finish(authctxt, authenticated, "gssapi-with-mic"); + } ++ ++Authmethod method_gsskeyex = { ++ "gssapi-keyex", ++ userauth_gsskeyex, ++ &options.gss_authentication ++}; + + Authmethod method_gssapi = { + "gssapi-with-mic", +Index: auth2.c +=================================================================== +RCS file: /cvs/openssh/auth2.c,v +retrieving revision 1.142 +diff -u -r1.142 auth2.c +--- auth2.c 5 Aug 2006 02:39:39 -0000 1.142 ++++ auth2.c 20 Dec 2006 10:41:42 -0000 +@@ -64,6 +64,7 @@ + extern Authmethod method_kbdint; + extern Authmethod method_hostbased; + #ifdef GSSAPI ++extern Authmethod method_gsskeyex; + extern Authmethod method_gssapi; + #endif + +@@ -71,6 +72,7 @@ + &method_none, + &method_pubkey, + #ifdef GSSAPI ++ &method_gsskeyex, + &method_gssapi, + #endif + &method_passwd, +@@ -199,6 +201,7 @@ + #endif + + authctxt->postponed = 0; ++ authctxt->server_caused_failure = 0; + + /* try to authenticate user */ + m = authmethod_lookup(method); +@@ -269,7 +272,9 @@ + /* now we can break out */ + authctxt->success = 1; + } else { +- if (authctxt->failures++ > options.max_authtries) { ++ /* Dont count server configuration issues against the client */ ++ if (!authctxt->server_caused_failure && ++ authctxt->failures++ > options.max_authtries) { + #ifdef SSH_AUDIT_EVENTS + PRIVSEP(audit_event(SSH_LOGIN_EXCEED_MAXTRIES)); + #endif +Index: configure.ac +=================================================================== +RCS file: /cvs/openssh/configure.ac,v +retrieving revision 1.370 +diff -u -r1.370 configure.ac +--- configure.ac 6 Oct 2006 23:07:21 -0000 1.370 ++++ configure.ac 20 Dec 2006 10:41:43 -0000 +@@ -403,7 +403,31 @@ + [Use tunnel device compatibility to OpenBSD]) + AC_DEFINE(SSH_TUN_PREPEND_AF, 1, + [Prepend the address family to IP tunnel traffic]) +- ;; ++ AC_MSG_CHECKING(if we have the Security Authorization Session API) ++ AC_TRY_COMPILE([#include <Security/AuthSession.h>], ++ [SessionCreate(0, 0);], ++ [ac_cv_use_security_session_api="yes" ++ AC_DEFINE(USE_SECURITY_SESSION_API, 1, ++ [platform has the Security Authorization Session API]) ++ LIBS="$LIBS -framework Security" ++ AC_MSG_RESULT(yes)], ++ [ac_cv_use_security_session_api="no" ++ AC_MSG_RESULT(no)]) ++ AC_MSG_CHECKING(if we have an in-memory credentials cache) ++ AC_TRY_COMPILE( ++ [#include <Kerberos/Kerberos.h>], ++ [cc_context_t c; ++ (void) cc_initialize (&c, 0, NULL, NULL);], ++ [AC_DEFINE(USE_CCAPI, 1, ++ [platform uses an in-memory credentials cache]) ++ LIBS="$LIBS -framework Security" ++ AC_MSG_RESULT(yes) ++ if test "x$ac_cv_use_security_session_api" = "xno"; then ++ AC_MSG_ERROR(*** Need a security framework to use the credentials cache API ***) ++ fi], ++ [AC_MSG_RESULT(no)] ++ ) ++ ;; + *-*-dragonfly*) + SSHDLIBS="$SSHDLIBS -lcrypt" + ;; +Index: gss-genr.c +=================================================================== +RCS file: /cvs/openssh/gss-genr.c,v +retrieving revision 1.19 +diff -u -r1.19 gss-genr.c +--- gss-genr.c 30 Aug 2006 01:08:04 -0000 1.19 ++++ gss-genr.c 20 Dec 2006 10:41:43 -0000 +@@ -39,12 +39,160 @@ + #include "buffer.h" + #include "log.h" + #include "ssh2.h" ++#include "cipher.h" ++#include "key.h" ++#include "kex.h" ++#include <openssl/evp.h> + + #include "ssh-gss.h" + + extern u_char *session_id2; + extern u_int session_id2_len; + ++typedef struct { ++ char *encoded; ++ gss_OID oid; ++} ssh_gss_kex_mapping; ++ ++/* ++ * XXX - It would be nice to find a more elegant way of handling the ++ * XXX passing of the key exchange context to the userauth routines ++ */ ++ ++Gssctxt *gss_kex_context = NULL; ++ ++static ssh_gss_kex_mapping *gss_enc2oid = NULL; ++ ++int ++ssh_gssapi_oid_table_ok() { ++ return (gss_enc2oid != NULL); ++} ++ ++/* ++ * Return a list of the gss-group1-sha1 mechanisms supported by this program ++ * ++ * We test mechanisms to ensure that we can use them, to avoid starting ++ * a key exchange with a bad mechanism ++ */ ++ ++char * ++ssh_gssapi_client_mechanisms(const char *host) { ++ gss_OID_set gss_supported; ++ OM_uint32 min_status; ++ ++ gss_indicate_mechs(&min_status, &gss_supported); ++ ++ return(ssh_gssapi_kex_mechs(gss_supported, ssh_gssapi_check_mechanism, ++ host)); ++} ++ ++char * ++ssh_gssapi_kex_mechs(gss_OID_set gss_supported, ssh_gssapi_check_fn *check, ++ const char *data) { ++ Buffer buf; ++ size_t i; ++ int oidpos, enclen; ++ char *mechs, *encoded; ++ u_char digest[EVP_MAX_MD_SIZE]; ++ char deroid[2]; ++ const EVP_MD *evp_md = EVP_md5(); ++ EVP_MD_CTX md; ++ ++ if (gss_enc2oid != NULL) { ++ for (i = 0; gss_enc2oid[i].encoded != NULL; i++) ++ xfree(gss_enc2oid[i].encoded); ++ xfree(gss_enc2oid); ++ } ++ ++ gss_enc2oid = xmalloc(sizeof(ssh_gss_kex_mapping) * ++ (gss_supported->count + 1)); ++ ++ buffer_init(&buf); ++ ++ oidpos = 0; ++ for (i = 0; i < gss_supported->count; i++) { ++ if (gss_supported->elements[i].length < 128 && ++ (*check)(NULL, &(gss_supported->elements[i]), data)) { ++ ++ deroid[0] = SSH_GSS_OIDTYPE; ++ deroid[1] = gss_supported->elements[i].length; ++ ++ EVP_DigestInit(&md, evp_md); ++ EVP_DigestUpdate(&md, deroid, 2); ++ EVP_DigestUpdate(&md, ++ gss_supported->elements[i].elements, ++ gss_supported->elements[i].length); ++ EVP_DigestFinal(&md, digest, NULL); ++ ++ encoded = xmalloc(EVP_MD_size(evp_md) * 2); ++ enclen = __b64_ntop(digest, EVP_MD_size(evp_md), ++ encoded, EVP_MD_size(evp_md) * 2); ++ ++ if (oidpos != 0) ++ buffer_put_char(&buf, ','); ++ ++ buffer_append(&buf, KEX_GSS_GEX_SHA1_ID, ++ sizeof(KEX_GSS_GEX_SHA1_ID) - 1); ++ buffer_append(&buf, encoded, enclen); ++ buffer_put_char(&buf, ','); ++ buffer_append(&buf, KEX_GSS_GRP1_SHA1_ID, ++ sizeof(KEX_GSS_GRP1_SHA1_ID) - 1); ++ buffer_append(&buf, encoded, enclen); ++ buffer_put_char(&buf, ','); ++ buffer_append(&buf, KEX_GSS_GRP14_SHA1_ID, ++ sizeof(KEX_GSS_GRP14_SHA1_ID) - 1); ++ buffer_append(&buf, encoded, enclen); ++ ++ gss_enc2oid[oidpos].oid = &(gss_supported->elements[i]); ++ gss_enc2oid[oidpos].encoded = encoded; ++ oidpos++; ++ } ++ } ++ gss_enc2oid[oidpos].oid = NULL; ++ gss_enc2oid[oidpos].encoded = NULL; ++ ++ buffer_put_char(&buf, '\0'); ++ ++ mechs = xmalloc(buffer_len(&buf)); ++ buffer_get(&buf, mechs, buffer_len(&buf)); ++ buffer_free(&buf); ++ ++ if (strlen(mechs) == 0) { ++ xfree(mechs); ++ mechs = NULL; ++ } ++ ++ return (mechs); ++} ++ ++gss_OID ++ssh_gssapi_id_kex(Gssctxt *ctx, char *name, int kex_type) { ++ int i = 0; ++ ++ switch (kex_type) { ++ case KEX_GSS_GRP1_SHA1: ++ name += sizeof(KEX_GSS_GRP1_SHA1_ID) - 1; ++ break; ++ case KEX_GSS_GRP14_SHA1: ++ name += sizeof(KEX_GSS_GRP14_SHA1_ID) - 1; ++ break; ++ case KEX_GSS_GEX_SHA1: ++ name += sizeof(KEX_GSS_GEX_SHA1_ID) - 1; ++ break; ++ default: ++ return GSS_C_NO_OID; ++ } ++ ++ while (gss_enc2oid[i].encoded != NULL && ++ strcmp(name, gss_enc2oid[i].encoded) != 0) ++ i++; ++ ++ if (gss_enc2oid[i].oid != NULL && ctx != NULL) ++ ssh_gssapi_set_oid(ctx, gss_enc2oid[i].oid); ++ ++ return gss_enc2oid[i].oid; ++} ++ + /* Check that the OID in a data stream matches that in the context */ + int + ssh_gssapi_check_oid(Gssctxt *ctx, void *data, size_t len) +@@ -107,7 +255,7 @@ + /* The GSSAPI error */ + do { + gss_display_status(&lmin, ctxt->major, +- GSS_C_GSS_CODE, GSS_C_NULL_OID, &ctx, &msg); ++ GSS_C_GSS_CODE, ctxt->oid, &ctx, &msg); + + buffer_append(&b, msg.value, msg.length); + buffer_put_char(&b, '\n'); +@@ -118,7 +266,7 @@ + /* The mechanism specific error */ + do { + gss_display_status(&lmin, ctxt->minor, +- GSS_C_MECH_CODE, GSS_C_NULL_OID, &ctx, &msg); ++ GSS_C_MECH_CODE, ctxt->oid, &ctx, &msg); + + buffer_append(&b, msg.value, msg.length); + buffer_put_char(&b, '\n'); +@@ -226,45 +374,28 @@ + return (ctx->major); + } + +-/* Acquire credentials for a server running on the current host. +- * Requires that the context structure contains a valid OID +- */ +- +-/* Returns a GSSAPI error code */ + OM_uint32 +-ssh_gssapi_acquire_cred(Gssctxt *ctx) ++ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash) + { +- OM_uint32 status; +- char lname[MAXHOSTNAMELEN]; +- gss_OID_set oidset; +- +- gss_create_empty_oid_set(&status, &oidset); +- gss_add_oid_set_member(&status, ctx->oid, &oidset); +- +- if (gethostname(lname, MAXHOSTNAMELEN)) { +- gss_release_oid_set(&status, &oidset); +- return (-1); +- } ++ if (ctx == NULL) ++ return -1; + +- if (GSS_ERROR(ssh_gssapi_import_name(ctx, lname))) { +- gss_release_oid_set(&status, &oidset); +- return (ctx->major); +- } +- +- if ((ctx->major = gss_acquire_cred(&ctx->minor, +- ctx->name, 0, oidset, GSS_C_ACCEPT, &ctx->creds, NULL, NULL))) ++ if ((ctx->major = gss_get_mic(&ctx->minor, ctx->context, ++ GSS_C_QOP_DEFAULT, buffer, hash))) + ssh_gssapi_error(ctx); + +- gss_release_oid_set(&status, &oidset); + return (ctx->major); + } + ++/* Priviledged when used by server */ + OM_uint32 +-ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash) ++ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic) + { +- if ((ctx->major = gss_get_mic(&ctx->minor, ctx->context, +- GSS_C_QOP_DEFAULT, buffer, hash))) +- ssh_gssapi_error(ctx); ++ if (ctx == NULL) ++ return -1; ++ ++ ctx->major = gss_verify_mic(&ctx->minor, ctx->context, ++ gssbuf, gssmic, NULL); + + return (ctx->major); + } +@@ -281,22 +412,16 @@ + buffer_put_cstring(b, context); + } + +-OM_uint32 +-ssh_gssapi_server_ctx(Gssctxt **ctx, gss_OID oid) +-{ +- if (*ctx) +- ssh_gssapi_delete_ctx(ctx); +- ssh_gssapi_build_ctx(ctx); +- ssh_gssapi_set_oid(*ctx, oid); +- return (ssh_gssapi_acquire_cred(*ctx)); +-} +- + int + ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host) + { + gss_buffer_desc token = GSS_C_EMPTY_BUFFER; + OM_uint32 major, minor; + gss_OID_desc spnego_oid = {6, (void *)"\x2B\x06\x01\x05\x05\x02"}; ++ Gssctxt *intctx = NULL; ++ ++ if (ctx == NULL) ++ ctx = &intctx; + + /* RFC 4462 says we MUST NOT do SPNEGO */ + if (oid->length == spnego_oid.length && +@@ -315,7 +440,7 @@ + GSS_C_NO_BUFFER); + } + +- if (GSS_ERROR(major)) ++ if (GSS_ERROR(major) || intctx != NULL) + ssh_gssapi_delete_ctx(ctx); + + return (!GSS_ERROR(major)); +Index: gss-serv-krb5.c +=================================================================== +RCS file: /cvs/openssh/gss-serv-krb5.c,v +retrieving revision 1.17 +diff -u -r1.17 gss-serv-krb5.c +--- gss-serv-krb5.c 1 Sep 2006 05:38:36 -0000 1.17 ++++ gss-serv-krb5.c 20 Dec 2006 10:41:43 -0000 +@@ -168,11 +168,16 @@ + return; + } + +- client->store.filename = xstrdup(krb5_cc_get_name(krb_context, ccache)); ++ const char* new_ccname = krb5_cc_get_name(krb_context, ccache); ++ + client->store.envvar = "KRB5CCNAME"; +- len = strlen(client->store.filename) + 6; +- client->store.envval = xmalloc(len); +- snprintf(client->store.envval, len, "FILE:%s", client->store.filename); ++#ifdef USE_CCAPI ++ xasprintf(&client->store.envval, "API:%s", new_ccname); ++ client->store.filename = NULL; ++#else ++ xasprintf(&client->store.envval, "FILE:%s", new_ccname); ++ client->store.filename = xstrdup(new_ccname); ++#endif + + #ifdef USE_PAM + if (options.use_pam) +Index: gss-serv.c +=================================================================== +RCS file: /cvs/openssh/gss-serv.c,v +retrieving revision 1.22 +diff -u -r1.22 gss-serv.c +--- gss-serv.c 1 Sep 2006 05:38:36 -0000 1.22 ++++ gss-serv.c 20 Dec 2006 10:41:43 -0000 +@@ -1,7 +1,7 @@ + /* $OpenBSD: gss-serv.c,v 1.20 2006/08/03 03:34:42 deraadt Exp $ */ + + /* +- * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. ++ * Copyright (c) 2001-2006 Simon Wilkinson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions +@@ -43,8 +43,12 @@ + #include "channels.h" + #include "session.h" + #include "misc.h" ++#include "servconf.h" + + #include "ssh-gss.h" ++#include "monitor_wrap.h" ++ ++extern ServerOptions options; + + static ssh_gssapi_client gssapi_client = + { GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER, +@@ -65,6 +69,28 @@ + }; + + /* Unprivileged */ ++char * ++ssh_gssapi_server_mechanisms() { ++ gss_OID_set supported; ++ ++ ssh_gssapi_supported_oids(&supported); ++ return (ssh_gssapi_kex_mechs(supported, &ssh_gssapi_server_check_mech, ++ NULL)); ++} ++ ++/* Unprivileged */ ++int ++ssh_gssapi_server_check_mech(Gssctxt **dum, gss_OID oid, const char *data) { ++ Gssctxt *ctx = NULL; ++ int res; ++ ++ res = !GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctx, oid))); ++ ssh_gssapi_delete_ctx(&ctx); ++ ++ return (res); ++} ++ ++/* Unprivileged */ + void + ssh_gssapi_supported_oids(gss_OID_set *oidset) + { +@@ -89,6 +115,56 @@ + gss_release_oid_set(&min_status, &supported); + } + ++OM_uint32 ++ssh_gssapi_server_ctx(Gssctxt **ctx, gss_OID oid) ++{ ++ if (*ctx) ++ ssh_gssapi_delete_ctx(ctx); ++ ssh_gssapi_build_ctx(ctx); ++ ssh_gssapi_set_oid(*ctx, oid); ++ return (ssh_gssapi_acquire_cred(*ctx)); ++} ++ ++/* Acquire credentials for a server running on the current host. ++ * Requires that the context structure contains a valid OID ++ */ ++ ++/* Returns a GSSAPI error code */ ++OM_uint32 ++ssh_gssapi_acquire_cred(Gssctxt *ctx) ++{ ++ OM_uint32 status; ++ char lname[MAXHOSTNAMELEN]; ++ gss_OID_set oidset; ++ ++ if (options.gss_strict_acceptor) { ++ gss_create_empty_oid_set(&status, &oidset); ++ gss_add_oid_set_member(&status, ctx->oid, &oidset); ++ ++ if (gethostname(lname, MAXHOSTNAMELEN)) { ++ gss_release_oid_set(&status, &oidset); ++ return (-1); ++ } ++ ++ if (GSS_ERROR(ssh_gssapi_import_name(ctx, lname))) { ++ gss_release_oid_set(&status, &oidset); ++ return (ctx->major); ++ } ++ ++ if ((ctx->major = gss_acquire_cred(&ctx->minor, ++ ctx->name, 0, oidset, GSS_C_ACCEPT, &ctx->creds, ++ NULL, NULL))) ++ ssh_gssapi_error(ctx); ++ ++ gss_release_oid_set(&status, &oidset); ++ return (ctx->major); ++ } else { ++ ctx->name = GSS_C_NO_NAME; ++ ctx->creds = GSS_C_NO_CREDENTIAL; ++ } ++ return GSS_S_COMPLETE; ++} ++ + + /* Wrapper around accept_sec_context + * Requires that the context contains: +@@ -301,16 +377,6 @@ + else + debug("ssh_gssapi_userok: Unknown GSSAPI mechanism"); + return (0); +-} +- +-/* Privileged */ +-OM_uint32 +-ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic) +-{ +- ctx->major = gss_verify_mic(&ctx->minor, ctx->context, +- gssbuf, gssmic, NULL); +- +- return (ctx->major); + } + + #endif +Index: kex.c +=================================================================== +RCS file: /cvs/openssh/kex.c,v +retrieving revision 1.83 +diff -u -r1.83 kex.c +--- kex.c 1 Sep 2006 05:38:36 -0000 1.83 ++++ kex.c 20 Dec 2006 10:41:43 -0000 +@@ -49,6 +49,10 @@ + #include "dispatch.h" + #include "monitor.h" + ++#ifdef GSSAPI ++#include "ssh-gss.h" ++#endif ++ + #define KEX_COOKIE_LEN 16 + + #if OPENSSL_VERSION_NUMBER >= 0x00907000L +@@ -322,6 +326,20 @@ + } else if (strcmp(k->name, KEX_DHGEX_SHA256) == 0) { + k->kex_type = KEX_DH_GEX_SHA256; + k->evp_md = evp_ssh_sha256(); ++#endif ++#ifdef GSSAPI ++ } else if (strncmp(k->name, KEX_GSS_GEX_SHA1_ID, ++ sizeof(KEX_GSS_GEX_SHA1_ID) - 1) == 0) { ++ k->kex_type = KEX_GSS_GEX_SHA1; ++ k->evp_md = EVP_sha1(); ++ } else if (strncmp(k->name, KEX_GSS_GRP1_SHA1_ID, ++ sizeof(KEX_GSS_GRP1_SHA1_ID) - 1) == 0) { ++ k->kex_type = KEX_GSS_GRP1_SHA1; ++ k->evp_md = EVP_sha1(); ++ } else if (strncmp(k->name, KEX_GSS_GRP14_SHA1_ID, ++ sizeof(KEX_GSS_GRP14_SHA1_ID) - 1) == 0) { ++ k->kex_type = KEX_GSS_GRP14_SHA1; ++ k->evp_md = EVP_sha1(); + #endif + } else + fatal("bad kex alg %s", k->name); +Index: kex.h +=================================================================== +RCS file: /cvs/openssh/kex.h,v +retrieving revision 1.47 +diff -u -r1.47 kex.h +--- kex.h 5 Aug 2006 02:39:40 -0000 1.47 ++++ kex.h 20 Dec 2006 10:41:43 -0000 +@@ -63,6 +63,9 @@ + KEX_DH_GRP14_SHA1, + KEX_DH_GEX_SHA1, + KEX_DH_GEX_SHA256, ++ KEX_GSS_GRP1_SHA1, ++ KEX_GSS_GRP14_SHA1, ++ KEX_GSS_GEX_SHA1, + KEX_MAX + }; + +@@ -115,6 +118,11 @@ + sig_atomic_t done; + int flags; + const EVP_MD *evp_md; ++#ifdef GSSAPI ++ int gss_deleg_creds; ++ int gss_trust_dns; ++ char *gss_host; ++#endif + char *client_version_string; + char *server_version_string; + int (*verify_host_key)(Key *); +@@ -136,6 +144,11 @@ + void kexdh_server(Kex *); + void kexgex_client(Kex *); + void kexgex_server(Kex *); ++ ++#ifdef GSSAPI ++void kexgss_client(Kex *); ++void kexgss_server(Kex *); ++#endif + + void + kex_dh_hash(char *, char *, char *, int, char *, int, u_char *, int, +Index: kexgssc.c +=================================================================== +RCS file: kexgssc.c +diff -N kexgssc.c +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ kexgssc.c 20 Dec 2006 10:41:44 -0000 +@@ -0,0 +1,319 @@ ++/* ++ * Copyright (c) 2001-2006 Simon Wilkinson. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ++ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ++ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, ++ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT ++ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF ++ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#include "includes.h" ++ ++#ifdef GSSAPI ++ ++#include "includes.h" ++ ++#include <openssl/crypto.h> ++#include <openssl/bn.h> ++ ++#include <string.h> ++ ++#include "xmalloc.h" ++#include "buffer.h" ++#include "ssh2.h" ++#include "key.h" ++#include "cipher.h" ++#include "kex.h" ++#include "log.h" ++#include "packet.h" ++#include "dh.h" ++ ++#include "ssh-gss.h" ++ ++void ++kexgss_client(Kex *kex) { ++ gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER; ++ gss_buffer_desc recv_tok, gssbuf, msg_tok, *token_ptr; ++ Gssctxt *ctxt; ++ OM_uint32 maj_status, min_status, ret_flags; ++ u_int klen, kout, slen = 0, hashlen, strlen; ++ DH *dh; ++ BIGNUM *dh_server_pub = NULL; ++ BIGNUM *shared_secret = NULL; ++ BIGNUM *p = NULL; ++ BIGNUM *g = NULL; ++ u_char *kbuf, *hash; ++ u_char *serverhostkey = NULL; ++ char *msg; ++ char *lang; ++ int type = 0; ++ int first = 1; ++ int nbits = 0, min = DH_GRP_MIN, max = DH_GRP_MAX; ++ ++ /* Initialise our GSSAPI world */ ++ ssh_gssapi_build_ctx(&ctxt); ++ if (ssh_gssapi_id_kex(ctxt, kex->name, kex->kex_type) ++ == GSS_C_NO_OID) ++ fatal("Couldn't identify host exchange"); ++ ++ if (ssh_gssapi_import_name(ctxt, kex->gss_host)) ++ fatal("Couldn't import hostname"); ++ ++ switch (kex->kex_type) { ++ case KEX_GSS_GRP1_SHA1: ++ dh = dh_new_group1(); ++ break; ++ case KEX_GSS_GRP14_SHA1: ++ dh = dh_new_group14(); ++ break; ++ case KEX_GSS_GEX_SHA1: ++ debug("Doing group exchange\n"); ++ nbits = dh_estimate(kex->we_need * 8); ++ packet_start(SSH2_MSG_KEXGSS_GROUPREQ); ++ packet_put_int(min); ++ packet_put_int(nbits); ++ packet_put_int(max); ++ ++ packet_send(); ++ ++ packet_read_expect(SSH2_MSG_KEXGSS_GROUP); ++ ++ if ((p = BN_new()) == NULL) ++ fatal("BN_new() failed"); ++ packet_get_bignum2(p); ++ if ((g = BN_new()) == NULL) ++ fatal("BN_new() failed"); ++ packet_get_bignum2(g); ++ packet_check_eom(); ++ ++ if (BN_num_bits(p) < min || BN_num_bits(p) > max) ++ fatal("GSSGRP_GEX group out of range: %d !< %d !< %d", ++ min, BN_num_bits(p), max); ++ ++ dh = dh_new_group(g, p); ++ break; ++ default: ++ fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); ++ } ++ ++ /* Step 1 - e is dh->pub_key */ ++ dh_gen_key(dh, kex->we_need * 8); ++ ++ /* This is f, we initialise it now to make life easier */ ++ dh_server_pub = BN_new(); ++ if (dh_server_pub == NULL) ++ fatal("dh_server_pub == NULL"); ++ ++ token_ptr = GSS_C_NO_BUFFER; ++ ++ do { ++ debug("Calling gss_init_sec_context"); ++ ++ maj_status = ssh_gssapi_init_ctx(ctxt, ++ kex->gss_deleg_creds, token_ptr, &send_tok, ++ &ret_flags); ++ ++ if (GSS_ERROR(maj_status)) { ++ if (send_tok.length != 0) { ++ packet_start(SSH2_MSG_KEXGSS_CONTINUE); ++ packet_put_string(send_tok.value, ++ send_tok.length); ++ } ++ fatal("gss_init_context failed"); ++ } ++ ++ /* If we've got an old receive buffer get rid of it */ ++ if (token_ptr != GSS_C_NO_BUFFER) ++ xfree(recv_tok.value); ++ ++ if (maj_status == GSS_S_COMPLETE) { ++ /* If mutual state flag is not true, kex fails */ ++ if (!(ret_flags & GSS_C_MUTUAL_FLAG)) ++ fatal("Mutual authentication failed"); ++ ++ /* If integ avail flag is not true kex fails */ ++ if (!(ret_flags & GSS_C_INTEG_FLAG)) ++ fatal("Integrity check failed"); ++ } ++ ++ /* ++ * If we have data to send, then the last message that we ++ * received cannot have been a 'complete'. ++ */ ++ if (send_tok.length != 0) { ++ if (first) { ++ packet_start(SSH2_MSG_KEXGSS_INIT); ++ packet_put_string(send_tok.value, ++ send_tok.length); ++ packet_put_bignum2(dh->pub_key); ++ first = 0; ++ } else { ++ packet_start(SSH2_MSG_KEXGSS_CONTINUE); ++ packet_put_string(send_tok.value, ++ send_tok.length); ++ } ++ packet_send(); ++ gss_release_buffer(&min_status, &send_tok); ++ ++ /* If we've sent them data, they should reply */ ++ do { ++ type = packet_read(); ++ if (type == SSH2_MSG_KEXGSS_HOSTKEY) { ++ debug("Received KEXGSS_HOSTKEY"); ++ if (serverhostkey) ++ fatal("Server host key received more than once"); ++ serverhostkey = ++ packet_get_string(&slen); ++ } ++ } while (type == SSH2_MSG_KEXGSS_HOSTKEY); ++ ++ switch (type) { ++ case SSH2_MSG_KEXGSS_CONTINUE: ++ debug("Received GSSAPI_CONTINUE"); ++ if (maj_status == GSS_S_COMPLETE) ++ fatal("GSSAPI Continue received from server when complete"); ++ recv_tok.value = packet_get_string(&strlen); ++ recv_tok.length = strlen; ++ break; ++ case SSH2_MSG_KEXGSS_COMPLETE: ++ debug("Received GSSAPI_COMPLETE"); ++ packet_get_bignum2(dh_server_pub); ++ msg_tok.value = packet_get_string(&strlen); ++ msg_tok.length = strlen; ++ ++ /* Is there a token included? */ ++ if (packet_get_char()) { ++ recv_tok.value= ++ packet_get_string(&strlen); ++ recv_tok.length = strlen; ++ /* If we're already complete - protocol error */ ++ if (maj_status == GSS_S_COMPLETE) ++ packet_disconnect("Protocol error: received token when complete"); ++ } else { ++ /* No token included */ ++ if (maj_status != GSS_S_COMPLETE) ++ packet_disconnect("Protocol error: did not receive final token"); ++ } ++ break; ++ case SSH2_MSG_KEXGSS_ERROR: ++ debug("Received Error"); ++ maj_status = packet_get_int(); ++ min_status = packet_get_int(); ++ msg = packet_get_string(NULL); ++ lang = packet_get_string(NULL); ++ fatal("GSSAPI Error: \n%.400s",msg); ++ default: ++ packet_disconnect("Protocol error: didn't expect packet type %d", ++ type); ++ } ++ token_ptr = &recv_tok; ++ } else { ++ /* No data, and not complete */ ++ if (maj_status != GSS_S_COMPLETE) ++ fatal("Not complete, and no token output"); ++ } ++ } while (maj_status & GSS_S_CONTINUE_NEEDED); ++ ++ /* ++ * We _must_ have received a COMPLETE message in reply from the ++ * server, which will have set dh_server_pub and msg_tok ++ */ ++ ++ if (type != SSH2_MSG_KEXGSS_COMPLETE) ++ fatal("Didn't receive a SSH2_MSG_KEXGSS_COMPLETE when I expected it"); ++ ++ /* Check f in range [1, p-1] */ ++ if (!dh_pub_is_valid(dh, dh_server_pub)) ++ packet_disconnect("bad server public DH value"); ++ ++ /* compute K=f^x mod p */ ++ klen = DH_size(dh); ++ kbuf = xmalloc(klen); ++ kout = DH_compute_key(kbuf, dh_server_pub, dh); ++ ++ shared_secret = BN_new(); ++ BN_bin2bn(kbuf,kout, shared_secret); ++ memset(kbuf, 0, klen); ++ xfree(kbuf); ++ ++ switch (kex->kex_type) { ++ case KEX_GSS_GRP1_SHA1: ++ case KEX_GSS_GRP14_SHA1: ++ kex_dh_hash( kex->client_version_string, ++ kex->server_version_string, ++ buffer_ptr(&kex->my), buffer_len(&kex->my), ++ buffer_ptr(&kex->peer), buffer_len(&kex->peer), ++ serverhostkey, slen, /* server host key */ ++ dh->pub_key, /* e */ ++ dh_server_pub, /* f */ ++ shared_secret, /* K */ ++ &hash, &hashlen ++ ); ++ break; ++ case KEX_GSS_GEX_SHA1: ++ kexgex_hash( ++ kex->evp_md, ++ kex->client_version_string, ++ kex->server_version_string, ++ buffer_ptr(&kex->my), buffer_len(&kex->my), ++ buffer_ptr(&kex->peer), buffer_len(&kex->peer), ++ serverhostkey, slen, ++ min, nbits, max, ++ dh->p, dh->g, ++ dh->pub_key, ++ dh_server_pub, ++ shared_secret, ++ &hash, &hashlen ++ ); ++ break; ++ default: ++ fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); ++ } ++ ++ gssbuf.value = hash; ++ gssbuf.length = hashlen; ++ ++ /* Verify that the hash matches the MIC we just got. */ ++ if (GSS_ERROR(ssh_gssapi_checkmic(ctxt, &gssbuf, &msg_tok))) ++ packet_disconnect("Hash's MIC didn't verify"); ++ ++ xfree(msg_tok.value); ++ ++ DH_free(dh); ++ if (serverhostkey) ++ xfree(serverhostkey); ++ BN_clear_free(dh_server_pub); ++ ++ /* save session id */ ++ if (kex->session_id == NULL) { ++ kex->session_id_len = hashlen; ++ kex->session_id = xmalloc(kex->session_id_len); ++ memcpy(kex->session_id, hash, kex->session_id_len); ++ } ++ ++ if (gss_kex_context == NULL) ++ gss_kex_context = ctxt; ++ else ++ ssh_gssapi_delete_ctx(&ctxt); ++ ++ kex_derive_keys(kex, hash, hashlen, shared_secret); ++ BN_clear_free(shared_secret); ++ kex_finish(kex); ++} ++ ++#endif /* GSSAPI */ +Index: kexgsss.c +=================================================================== +RCS file: kexgsss.c +diff -N kexgsss.c +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ kexgsss.c 20 Dec 2006 10:41:44 -0000 +@@ -0,0 +1,271 @@ ++/* ++ * Copyright (c) 2001-2006 Simon Wilkinson. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ++ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ++ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, ++ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT ++ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF ++ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#include "includes.h" ++ ++#ifdef GSSAPI ++ ++#include <string.h> ++ ++#include <openssl/crypto.h> ++#include <openssl/bn.h> ++ ++#include "xmalloc.h" ++#include "buffer.h" ++#include "ssh2.h" ++#include "key.h" ++#include "cipher.h" ++#include "kex.h" ++#include "log.h" ++#include "packet.h" ++#include "dh.h" ++#include "ssh-gss.h" ++#include "monitor_wrap.h" ++ ++void ++kexgss_server(Kex *kex) ++{ ++ OM_uint32 maj_status, min_status; ++ ++ /* ++ * Some GSSAPI implementations use the input value of ret_flags (an ++ * output variable) as a means of triggering mechanism specific ++ * features. Initializing it to zero avoids inadvertently ++ * activating this non-standard behaviour. ++ */ ++ ++ OM_uint32 ret_flags = 0; ++ gss_buffer_desc gssbuf, recv_tok, msg_tok; ++ gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER; ++ Gssctxt *ctxt = NULL; ++ u_int slen, klen, kout, hashlen; ++ u_char *kbuf, *hash; ++ DH *dh; ++ int min = -1, max = -1, nbits = -1; ++ BIGNUM *shared_secret = NULL; ++ BIGNUM *dh_client_pub = NULL; ++ int type = 0; ++ gss_OID oid; ++ ++ /* Initialise GSSAPI */ ++ ++ /* If we're rekeying, privsep means that some of the private structures ++ * in the GSSAPI code are no longer available. This kludges them back ++ * into life ++ */ ++ if (!ssh_gssapi_oid_table_ok()) ++ ssh_gssapi_server_mechanisms(); ++ ++ debug2("%s: Identifying %s", __func__, kex->name); ++ oid = ssh_gssapi_id_kex(NULL, kex->name, kex->kex_type); ++ if (oid == GSS_C_NO_OID) ++ fatal("Unknown gssapi mechanism"); ++ ++ debug2("%s: Acquiring credentials", __func__); ++ ++ if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, oid)))) ++ fatal("Unable to acquire credentials for the server"); ++ ++ switch (kex->kex_type) { ++ case KEX_GSS_GRP1_SHA1: ++ dh = dh_new_group1(); ++ break; ++ case KEX_GSS_GRP14_SHA1: ++ dh = dh_new_group14(); ++ break; ++ case KEX_GSS_GEX_SHA1: ++ debug("Doing group exchange"); ++ packet_read_expect(SSH2_MSG_KEXGSS_GROUPREQ); ++ min = packet_get_int(); ++ nbits = packet_get_int(); ++ max = packet_get_int(); ++ min = MAX(DH_GRP_MIN, min); ++ max = MIN(DH_GRP_MAX, max); ++ packet_check_eom(); ++ if (max < min || nbits < min || max < nbits) ++ fatal("GSS_GEX, bad parameters: %d !< %d !< %d", ++ min, nbits, max); ++ dh = PRIVSEP(choose_dh(min, nbits, max)); ++ if (dh == NULL) ++ packet_disconnect("Protocol error: no matching group found"); ++ ++ packet_start(SSH2_MSG_KEXGSS_GROUP); ++ packet_put_bignum2(dh->p); ++ packet_put_bignum2(dh->g); ++ packet_send(); ++ ++ packet_write_wait(); ++ break; ++ default: ++ fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); ++ } ++ ++ dh_gen_key(dh, kex->we_need * 8); ++ ++ do { ++ debug("Wait SSH2_MSG_GSSAPI_INIT"); ++ type = packet_read(); ++ switch(type) { ++ case SSH2_MSG_KEXGSS_INIT: ++ if (dh_client_pub != NULL) ++ fatal("Received KEXGSS_INIT after initialising"); ++ recv_tok.value = packet_get_string(&slen); ++ recv_tok.length = slen; ++ ++ if ((dh_client_pub = BN_new()) == NULL) ++ fatal("dh_client_pub == NULL"); ++ ++ packet_get_bignum2(dh_client_pub); ++ ++ /* Send SSH_MSG_KEXGSS_HOSTKEY here, if we want */ ++ break; ++ case SSH2_MSG_KEXGSS_CONTINUE: ++ recv_tok.value = packet_get_string(&slen); ++ recv_tok.length = slen; ++ break; ++ default: ++ packet_disconnect( ++ "Protocol error: didn't expect packet type %d", ++ type); ++ } ++ ++ maj_status = PRIVSEP(ssh_gssapi_accept_ctx(ctxt, &recv_tok, ++ &send_tok, &ret_flags)); ++ ++ xfree(recv_tok.value); ++ ++ if (maj_status != GSS_S_COMPLETE && send_tok.length == 0) ++ fatal("Zero length token output when incomplete"); ++ ++ if (dh_client_pub == NULL) ++ fatal("No client public key"); ++ ++ if (maj_status & GSS_S_CONTINUE_NEEDED) { ++ debug("Sending GSSAPI_CONTINUE"); ++ packet_start(SSH2_MSG_KEXGSS_CONTINUE); ++ packet_put_string(send_tok.value, send_tok.length); ++ packet_send(); ++ gss_release_buffer(&min_status, &send_tok); ++ } ++ } while (maj_status & GSS_S_CONTINUE_NEEDED); ++ ++ if (GSS_ERROR(maj_status)) { ++ if (send_tok.length > 0) { ++ packet_start(SSH2_MSG_KEXGSS_CONTINUE); ++ packet_put_string(send_tok.value, send_tok.length); ++ packet_send(); ++ } ++ fatal("accept_ctx died"); ++ } ++ ++ if (!(ret_flags & GSS_C_MUTUAL_FLAG)) ++ fatal("Mutual Authentication flag wasn't set"); ++ ++ if (!(ret_flags & GSS_C_INTEG_FLAG)) ++ fatal("Integrity flag wasn't set"); ++ ++ if (!dh_pub_is_valid(dh, dh_client_pub)) ++ packet_disconnect("bad client public DH value"); ++ ++ klen = DH_size(dh); ++ kbuf = xmalloc(klen); ++ kout = DH_compute_key(kbuf, dh_client_pub, dh); ++ ++ shared_secret = BN_new(); ++ BN_bin2bn(kbuf, kout, shared_secret); ++ memset(kbuf, 0, klen); ++ xfree(kbuf); ++ ++ switch (kex->kex_type) { ++ case KEX_GSS_GRP1_SHA1: ++ case KEX_GSS_GRP14_SHA1: ++ kex_dh_hash( ++ kex->client_version_string, kex->server_version_string, ++ buffer_ptr(&kex->peer), buffer_len(&kex->peer), ++ buffer_ptr(&kex->my), buffer_len(&kex->my), ++ NULL, 0, /* Change this if we start sending host keys */ ++ dh_client_pub, dh->pub_key, shared_secret, ++ &hash, &hashlen ++ ); ++ break; ++ case KEX_GSS_GEX_SHA1: ++ kexgex_hash( ++ kex->evp_md, ++ kex->client_version_string, kex->server_version_string, ++ buffer_ptr(&kex->peer), buffer_len(&kex->peer), ++ buffer_ptr(&kex->my), buffer_len(&kex->my), ++ NULL, 0, ++ min, nbits, max, ++ dh->p, dh->g, ++ dh_client_pub, ++ dh->pub_key, ++ shared_secret, ++ &hash, &hashlen ++ ); ++ break; ++ default: ++ fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); ++ } ++ ++ BN_free(dh_client_pub); ++ ++ if (kex->session_id == NULL) { ++ kex->session_id_len = hashlen; ++ kex->session_id = xmalloc(kex->session_id_len); ++ memcpy(kex->session_id, hash, kex->session_id_len); ++ } ++ ++ gssbuf.value = hash; ++ gssbuf.length = hashlen; ++ ++ if (GSS_ERROR(PRIVSEP(ssh_gssapi_sign(ctxt,&gssbuf,&msg_tok)))) ++ fatal("Couldn't get MIC"); ++ ++ packet_start(SSH2_MSG_KEXGSS_COMPLETE); ++ packet_put_bignum2(dh->pub_key); ++ packet_put_string(msg_tok.value,msg_tok.length); ++ ++ if (send_tok.length != 0) { ++ packet_put_char(1); /* true */ ++ packet_put_string(send_tok.value, send_tok.length); ++ } else { ++ packet_put_char(0); /* false */ ++ } ++ packet_send(); ++ ++ gss_release_buffer(&min_status, &send_tok); ++ gss_release_buffer(&min_status, &msg_tok); ++ ++ if (gss_kex_context == NULL) ++ gss_kex_context = ctxt; ++ else ++ ssh_gssapi_delete_ctx(&ctxt); ++ ++ DH_free(dh); ++ ++ kex_derive_keys(kex, hash, hashlen, shared_secret); ++ BN_clear_free(shared_secret); ++ kex_finish(kex); ++} ++#endif /* GSSAPI */ +Index: key.c +=================================================================== +RCS file: /cvs/openssh/key.c,v +retrieving revision 1.70 +diff -u -r1.70 key.c +--- key.c 7 Nov 2006 12:14:42 -0000 1.70 ++++ key.c 20 Dec 2006 10:41:44 -0000 +@@ -650,6 +650,8 @@ + return KEY_RSA; + } else if (strcmp(name, "ssh-dss") == 0) { + return KEY_DSA; ++ } else if (strcmp(name, "null") == 0) { ++ return KEY_NULL; + } + debug2("key_type_from_name: unknown key type '%s'", name); + return KEY_UNSPEC; +Index: key.h +=================================================================== +RCS file: /cvs/openssh/key.h,v +retrieving revision 1.28 +diff -u -r1.28 key.h +--- key.h 5 Aug 2006 02:39:40 -0000 1.28 ++++ key.h 20 Dec 2006 10:41:44 -0000 +@@ -34,6 +34,7 @@ + KEY_RSA1, + KEY_RSA, + KEY_DSA, ++ KEY_NULL, + KEY_UNSPEC + }; + enum fp_type { +Index: monitor.c +=================================================================== +RCS file: /cvs/openssh/monitor.c,v +retrieving revision 1.121 +diff -u -r1.121 monitor.c +--- monitor.c 7 Nov 2006 12:16:08 -0000 1.121 ++++ monitor.c 20 Dec 2006 10:41:44 -0000 +@@ -163,6 +163,7 @@ + int mm_answer_gss_accept_ctx(int, Buffer *); + int mm_answer_gss_userok(int, Buffer *); + int mm_answer_gss_checkmic(int, Buffer *); ++int mm_answer_gss_sign(int, Buffer *); + #endif + + #ifdef SSH_AUDIT_EVENTS +@@ -232,11 +233,17 @@ + {MONITOR_REQ_GSSSTEP, MON_ISAUTH, mm_answer_gss_accept_ctx}, + {MONITOR_REQ_GSSUSEROK, MON_AUTH, mm_answer_gss_userok}, + {MONITOR_REQ_GSSCHECKMIC, MON_ISAUTH, mm_answer_gss_checkmic}, ++ {MONITOR_REQ_GSSSIGN, MON_ONCE, mm_answer_gss_sign}, + #endif + {0, 0, NULL} + }; + + struct mon_table mon_dispatch_postauth20[] = { ++#ifdef GSSAPI ++ {MONITOR_REQ_GSSSETUP, 0, mm_answer_gss_setup_ctx}, ++ {MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx}, ++ {MONITOR_REQ_GSSSIGN, 0, mm_answer_gss_sign}, ++#endif + {MONITOR_REQ_MODULI, 0, mm_answer_moduli}, + {MONITOR_REQ_SIGN, 0, mm_answer_sign}, + {MONITOR_REQ_PTY, 0, mm_answer_pty}, +@@ -341,6 +348,10 @@ + /* Permit requests for moduli and signatures */ + monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1); + monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1); ++#ifdef GSSAPI ++ /* and for the GSSAPI key exchange */ ++ monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1); ++#endif + } else { + mon_dispatch = mon_dispatch_proto15; + +@@ -417,6 +428,10 @@ + monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1); + monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1); + monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1); ++#ifdef GSSAPI ++ /* and for the GSSAPI key exchange */ ++ monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1); ++#endif + } else { + mon_dispatch = mon_dispatch_postauth15; + monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1); +@@ -1660,6 +1675,11 @@ + kex->kex[KEX_DH_GRP14_SHA1] = kexdh_server; + kex->kex[KEX_DH_GEX_SHA1] = kexgex_server; + kex->kex[KEX_DH_GEX_SHA256] = kexgex_server; ++#ifdef GSSAPI ++ kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server; ++ kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server; ++ kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server; ++#endif + kex->server = 1; + kex->hostkey_type = buffer_get_int(m); + kex->kex_type = buffer_get_int(m); +@@ -1901,6 +1921,7 @@ + monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTEP, 0); + monitor_permit(mon_dispatch, MONITOR_REQ_GSSUSEROK, 1); + monitor_permit(mon_dispatch, MONITOR_REQ_GSSCHECKMIC, 1); ++ monitor_permit(mon_dispatch, MONITOR_REQ_GSSSIGN, 1); + } + return (0); + } +@@ -1951,4 +1972,42 @@ + /* Monitor loop will terminate if authenticated */ + return (authenticated); + } ++ ++int ++mm_answer_gss_sign(int socket, Buffer *m) ++{ ++ gss_buffer_desc data; ++ gss_buffer_desc hash = GSS_C_EMPTY_BUFFER; ++ OM_uint32 major, minor; ++ u_int len; ++ ++ data.value = buffer_get_string(m, &len); ++ data.length = len; ++ if (data.length != 20) ++ fatal("%s: data length incorrect: %d", __func__, data.length); ++ ++ /* Save the session ID on the first time around */ ++ if (session_id2_len == 0) { ++ session_id2_len = data.length; ++ session_id2 = xmalloc(session_id2_len); ++ memcpy(session_id2, data.value, session_id2_len); ++ } ++ major = ssh_gssapi_sign(gsscontext, &data, &hash); ++ ++ xfree(data.value); ++ ++ buffer_clear(m); ++ buffer_put_int(m, major); ++ buffer_put_string(m, hash.value, hash.length); ++ ++ mm_request_send(socket, MONITOR_ANS_GSSSIGN, m); ++ ++ gss_release_buffer(&minor, &hash); ++ ++ /* Turn on getpwnam permissions */ ++ monitor_permit(mon_dispatch, MONITOR_REQ_PWNAM, 1); ++ ++ return (0); ++} ++ + #endif /* GSSAPI */ +Index: monitor.h +=================================================================== +RCS file: /cvs/openssh/monitor.h,v +retrieving revision 1.21 +diff -u -r1.21 monitor.h +--- monitor.h 26 Mar 2006 03:30:02 -0000 1.21 ++++ monitor.h 20 Dec 2006 10:41:44 -0000 +@@ -53,6 +53,7 @@ + MONITOR_REQ_GSSSTEP, MONITOR_ANS_GSSSTEP, + MONITOR_REQ_GSSUSEROK, MONITOR_ANS_GSSUSEROK, + MONITOR_REQ_GSSCHECKMIC, MONITOR_ANS_GSSCHECKMIC, ++ MONITOR_REQ_GSSSIGN, MONITOR_ANS_GSSSIGN, + MONITOR_REQ_PAM_START, + MONITOR_REQ_PAM_ACCOUNT, MONITOR_ANS_PAM_ACCOUNT, + MONITOR_REQ_PAM_INIT_CTX, MONITOR_ANS_PAM_INIT_CTX, +Index: monitor_wrap.c +=================================================================== +RCS file: /cvs/openssh/monitor_wrap.c,v +retrieving revision 1.70 +diff -u -r1.70 monitor_wrap.c +--- monitor_wrap.c 1 Sep 2006 05:38:37 -0000 1.70 ++++ monitor_wrap.c 20 Dec 2006 10:41:44 -0000 +@@ -1224,4 +1224,27 @@ + debug3("%s: user %sauthenticated",__func__, authenticated ? "" : "not "); + return (authenticated); + } ++ ++OM_uint32 ++mm_ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_desc *data, gss_buffer_desc *hash) ++{ ++ Buffer m; ++ OM_uint32 major; ++ u_int len; ++ ++ buffer_init(&m); ++ buffer_put_string(&m, data->value, data->length); ++ ++ mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSSIGN, &m); ++ mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSSIGN, &m); ++ ++ major = buffer_get_int(&m); ++ hash->value = buffer_get_string(&m, &len); ++ hash->length = len; ++ ++ buffer_free(&m); ++ ++ return(major); ++} ++ + #endif /* GSSAPI */ +Index: monitor_wrap.h +=================================================================== +RCS file: /cvs/openssh/monitor_wrap.h,v +retrieving revision 1.27 +diff -u -r1.27 monitor_wrap.h +--- monitor_wrap.h 5 Aug 2006 02:39:40 -0000 1.27 ++++ monitor_wrap.h 20 Dec 2006 10:41:44 -0000 +@@ -59,6 +59,7 @@ + gss_buffer_desc *, gss_buffer_desc *, OM_uint32 *); + int mm_ssh_gssapi_userok(char *user); + OM_uint32 mm_ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t); ++OM_uint32 mm_ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t); + #endif + + #ifdef USE_PAM +Index: readconf.c +=================================================================== +RCS file: /cvs/openssh/readconf.c,v +retrieving revision 1.136 +diff -u -r1.136 readconf.c +--- readconf.c 1 Sep 2006 05:38:37 -0000 1.136 ++++ readconf.c 20 Dec 2006 10:41:44 -0000 +@@ -127,6 +127,8 @@ + oClearAllForwardings, oNoHostAuthenticationForLocalhost, + oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout, + oAddressFamily, oGssAuthentication, oGssDelegateCreds, ++ oGssKeyEx, ++ oGssTrustDns, + oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly, + oSendEnv, oControlPath, oControlMaster, oHashKnownHosts, + oTunnel, oTunnelDevice, oLocalCommand, oPermitLocalCommand, +@@ -163,10 +165,14 @@ + { "afstokenpassing", oUnsupported }, + #if defined(GSSAPI) + { "gssapiauthentication", oGssAuthentication }, ++ { "gssapikeyexchange", oGssKeyEx }, + { "gssapidelegatecredentials", oGssDelegateCreds }, ++ { "gssapitrustdns", oGssTrustDns }, + #else + { "gssapiauthentication", oUnsupported }, ++ { "gssapikeyexchange", oUnsupported }, + { "gssapidelegatecredentials", oUnsupported }, ++ { "gssapitrustdns", oUnsupported }, + #endif + { "fallbacktorsh", oDeprecated }, + { "usersh", oDeprecated }, +@@ -441,10 +447,18 @@ + intptr = &options->gss_authentication; + goto parse_flag; + ++ case oGssKeyEx: ++ intptr = &options->gss_keyex; ++ goto parse_flag; ++ + case oGssDelegateCreds: + intptr = &options->gss_deleg_creds; + goto parse_flag; + ++ case oGssTrustDns: ++ intptr = &options->gss_trust_dns; ++ goto parse_flag; ++ + case oBatchMode: + intptr = &options->batch_mode; + goto parse_flag; +@@ -1010,7 +1024,9 @@ + options->pubkey_authentication = -1; + options->challenge_response_authentication = -1; + options->gss_authentication = -1; ++ options->gss_keyex = -1; + options->gss_deleg_creds = -1; ++ options->gss_trust_dns = -1; + options->password_authentication = -1; + options->kbd_interactive_authentication = -1; + options->kbd_interactive_devices = NULL; +@@ -1099,8 +1115,12 @@ + options->challenge_response_authentication = 1; + if (options->gss_authentication == -1) + options->gss_authentication = 0; ++ if (options->gss_keyex == -1) ++ options->gss_keyex = 0; + if (options->gss_deleg_creds == -1) + options->gss_deleg_creds = 0; ++ if (options->gss_trust_dns == -1) ++ options->gss_trust_dns = 0; + if (options->password_authentication == -1) + options->password_authentication = 1; + if (options->kbd_interactive_authentication == -1) +Index: readconf.h +=================================================================== +RCS file: /cvs/openssh/readconf.h,v +retrieving revision 1.63 +diff -u -r1.63 readconf.h +--- readconf.h 5 Aug 2006 02:39:40 -0000 1.63 ++++ readconf.h 20 Dec 2006 10:41:44 -0000 +@@ -44,7 +44,9 @@ + int challenge_response_authentication; + /* Try S/Key or TIS, authentication. */ + int gss_authentication; /* Try GSS authentication */ ++ int gss_keyex; /* Try GSS key exchange */ + int gss_deleg_creds; /* Delegate GSS credentials */ ++ int gss_trust_dns; /* Trust DNS for GSS canonicalization */ + int password_authentication; /* Try password + * authentication. */ + int kbd_interactive_authentication; /* Try keyboard-interactive auth. */ +--- servconf.c.orig Thu Mar 1 07:31:29 2007 ++++ servconf.c Mon Mar 12 18:09:49 2007 +@@ -90,7 +90,9 @@ + options->kerberos_ticket_cleanup = -1; + options->kerberos_get_afs_token = -1; + options->gss_authentication=-1; ++ options->gss_keyex = -1; + options->gss_cleanup_creds = -1; ++ options->gss_strict_acceptor = -1; + options->password_authentication = -1; + options->kbd_interactive_authentication = -1; + options->challenge_response_authentication = -1; +@@ -204,8 +206,12 @@ + options->kerberos_get_afs_token = 0; + if (options->gss_authentication == -1) + options->gss_authentication = 0; ++ if (options->gss_keyex == -1) ++ options->gss_keyex = 0; + if (options->gss_cleanup_creds == -1) + options->gss_cleanup_creds = 1; ++ if (options->gss_strict_acceptor == -1) ++ options->gss_strict_acceptor = 1; + if (options->password_authentication == -1) + options->password_authentication = 1; + if (options->kbd_interactive_authentication == -1) +@@ -290,7 +296,9 @@ + sBanner, sUseDNS, sHostbasedAuthentication, + sHostbasedUsesNameFromPacketOnly, sClientAliveInterval, + sClientAliveCountMax, sAuthorizedKeysFile, sAuthorizedKeysFile2, +- sGssAuthentication, sGssCleanupCreds, sAcceptEnv, sPermitTunnel, ++ sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor, ++ sGssKeyEx, ++ sAcceptEnv, sPermitTunnel, + sMatch, sPermitOpen, sForceCommand, + sUsePrivilegeSeparation, + sDeprecated, sUnsupported +@@ -351,9 +359,13 @@ + #ifdef GSSAPI + { "gssapiauthentication", sGssAuthentication, SSHCFG_ALL }, + { "gssapicleanupcredentials", sGssCleanupCreds, SSHCFG_GLOBAL }, ++ { "gssapistrictacceptorcheck", sGssStrictAcceptor, SSHCFG_GLOBAL }, ++ { "gssapikeyexchange", sGssKeyEx, SSHCFG_GLOBAL }, + #else + { "gssapiauthentication", sUnsupported, SSHCFG_ALL }, + { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL }, ++ { "gssapistrictacceptorcheck", sUnsupported, SSHCFG_GLOBAL }, ++ { "gssapikeyexchange", sUnsupported, SSHCFG_GLOBAL }, + #endif + { "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL }, + { "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL }, +@@ -872,8 +884,16 @@ + intptr = &options->gss_authentication; + goto parse_flag; + ++ case sGssKeyEx: ++ intptr = &options->gss_keyex; ++ goto parse_flag; ++ + case sGssCleanupCreds: + intptr = &options->gss_cleanup_creds; ++ goto parse_flag; ++ ++ case sGssStrictAcceptor: ++ intptr = &options->gss_strict_acceptor; + goto parse_flag; + + case sPasswordAuthentication: +Index: servconf.h +=================================================================== +RCS file: /cvs/openssh/servconf.h,v +retrieving revision 1.71 +diff -u -r1.71 servconf.h +--- servconf.h 18 Aug 2006 14:23:15 -0000 1.71 ++++ servconf.h 20 Dec 2006 10:41:45 -0000 +@@ -87,7 +87,9 @@ + int kerberos_get_afs_token; /* If true, try to get AFS token if + * authenticated with Kerberos. */ + int gss_authentication; /* If true, permit GSSAPI authentication */ ++ int gss_keyex; /* If true, permit GSSAPI key exchange */ + int gss_cleanup_creds; /* If true, destroy cred cache on logout */ ++ int gss_strict_acceptor; /* If true, restrict the GSSAPI acceptor name */ + int password_authentication; /* If true, permit password + * authentication. */ + int kbd_interactive_authentication; /* If true, permit */ +Index: ssh-gss.h +=================================================================== +RCS file: /cvs/openssh/ssh-gss.h,v +retrieving revision 1.11 +diff -u -r1.11 ssh-gss.h +--- ssh-gss.h 18 Aug 2006 14:46:44 -0000 1.11 ++++ ssh-gss.h 20 Dec 2006 10:41:45 -0000 +@@ -60,6 +60,17 @@ + + #define SSH_GSS_OIDTYPE 0x06 + ++#define SSH2_MSG_KEXGSS_INIT 30 ++#define SSH2_MSG_KEXGSS_CONTINUE 31 ++#define SSH2_MSG_KEXGSS_COMPLETE 32 ++#define SSH2_MSG_KEXGSS_HOSTKEY 33 ++#define SSH2_MSG_KEXGSS_ERROR 34 ++#define SSH2_MSG_KEXGSS_GROUPREQ 40 ++#define SSH2_MSG_KEXGSS_GROUP 41 ++#define KEX_GSS_GRP1_SHA1_ID "gss-group1-sha1-" ++#define KEX_GSS_GRP14_SHA1_ID "gss-group14-sha1-" ++#define KEX_GSS_GEX_SHA1_ID "gss-gex-sha1-" ++ + typedef struct { + char *filename; + char *envvar; +@@ -97,6 +108,7 @@ + } Gssctxt; + + extern ssh_gssapi_mech *supported_mechs[]; ++extern Gssctxt *gss_kex_context; + + int ssh_gssapi_check_oid(Gssctxt *, void *, size_t); + void ssh_gssapi_set_oid_data(Gssctxt *, void *, size_t); +@@ -121,12 +133,19 @@ + int ssh_gssapi_check_mechanism(Gssctxt **, gss_OID, const char *); + + /* In the server */ ++typedef int ssh_gssapi_check_fn(Gssctxt **, gss_OID, const char *); ++char *ssh_gssapi_client_mechanisms(const char *host); ++char *ssh_gssapi_kex_mechs(gss_OID_set, ssh_gssapi_check_fn *, const char *); ++gss_OID ssh_gssapi_id_kex(Gssctxt *, char *, int); ++int ssh_gssapi_server_check_mech(Gssctxt **,gss_OID, const char *); + int ssh_gssapi_userok(char *name); + OM_uint32 ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t); + void ssh_gssapi_do_child(char ***, u_int *); + void ssh_gssapi_cleanup_creds(void); + void ssh_gssapi_storecreds(void); + ++char *ssh_gssapi_server_mechanisms(void); ++int ssh_gssapi_oid_table_ok(); + #endif /* GSSAPI */ + + #endif /* _SSH_GSS_H */ +Index: ssh_config +=================================================================== +RCS file: /cvs/openssh/ssh_config,v +retrieving revision 1.24 +diff -u -r1.24 ssh_config +--- ssh_config 13 Jun 2006 03:01:10 -0000 1.24 ++++ ssh_config 20 Dec 2006 10:41:45 -0000 +@@ -26,6 +26,8 @@ + # HostbasedAuthentication no + # GSSAPIAuthentication no + # GSSAPIDelegateCredentials no ++# GSSAPIKeyExchange no ++# GSSAPITrustDNS no + # BatchMode no + # CheckHostIP yes + # AddressFamily any +Index: ssh_config.5 +=================================================================== +RCS file: /cvs/openssh/ssh_config.5,v +retrieving revision 1.97 +diff -u -r1.97 ssh_config.5 +--- ssh_config.5 5 Aug 2006 01:34:51 -0000 1.97 ++++ ssh_config.5 20 Dec 2006 10:41:45 -0000 +@@ -479,11 +479,28 @@ + The default is + .Dq no . + Note that this option applies to protocol version 2 only. ++.It Cm GSSAPIKeyExchange ++Specifies whether key exchange based on GSSAPI may be used. When using ++GSSAPI key exchange the server need not have a host key. ++The default is ++.Dq no . ++Note that this option applies to protocol version 2 only. + .It Cm GSSAPIDelegateCredentials + Forward (delegate) credentials to the server. + The default is + .Dq no . + Note that this option applies to protocol version 2 only. ++.It Cm GSSAPITrustDns ++Set to ++.Dq yes ++to indicate that the DNS is trusted to securely canonicalize ++the name of the host being connected to. If ++.Dq no , ++the hostname entered on the ++command line will be passed untouched to the GSSAPI library. ++The default is ++.Dq no . ++This option only applies to protocol version 2 connections using GSSAPI. + .It Cm HashKnownHosts + Indicates that + .Xr ssh 1 +Index: sshconnect2.c +=================================================================== +RCS file: /cvs/openssh/sshconnect2.c,v +retrieving revision 1.153 +diff -u -r1.153 sshconnect2.c +--- sshconnect2.c 1 Sep 2006 05:38:37 -0000 1.153 ++++ sshconnect2.c 20 Dec 2006 10:41:45 -0000 +@@ -98,9 +98,34 @@ + { + Kex *kex; + ++#ifdef GSSAPI ++ char *orig = NULL, *gss = NULL; ++ char *gss_host = NULL; ++#endif ++ + xxx_host = host; + xxx_hostaddr = hostaddr; + ++#ifdef GSSAPI ++ if (options.gss_keyex) { ++ /* Add the GSSAPI mechanisms currently supported on this ++ * client to the key exchange algorithm proposal */ ++ orig = myproposal[PROPOSAL_KEX_ALGS]; ++ ++ if (options.gss_trust_dns) ++ gss_host = (char *)get_canonical_hostname(1); ++ else ++ gss_host = host; ++ ++ gss = ssh_gssapi_client_mechanisms(gss_host); ++ if (gss) { ++ debug("Offering GSSAPI proposal: %s", gss); ++ xasprintf(&myproposal[PROPOSAL_KEX_ALGS], ++ "%s,%s", gss, orig); ++ } ++ } ++#endif ++ + if (options.ciphers == (char *)-1) { + logit("No valid ciphers for protocol version 2 given, using defaults."); + options.ciphers = NULL; +@@ -128,6 +153,16 @@ + myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = + options.hostkeyalgorithms; + ++#ifdef GSSAPI ++ /* If we've got GSSAPI algorithms, then we also support the ++ * 'null' hostkey, as a last resort */ ++ if (options.gss_keyex && gss) { ++ orig = myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]; ++ xasprintf(&myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS], ++ "%s,null", orig); ++ } ++#endif ++ + if (options.rekey_limit) + packet_set_rekey_limit(options.rekey_limit); + +@@ -137,10 +172,21 @@ + kex->kex[KEX_DH_GRP14_SHA1] = kexdh_client; + kex->kex[KEX_DH_GEX_SHA1] = kexgex_client; + kex->kex[KEX_DH_GEX_SHA256] = kexgex_client; ++#ifdef GSSAPI ++ kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_client; ++ kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_client; ++ kex->kex[KEX_GSS_GEX_SHA1] = kexgss_client; ++#endif + kex->client_version_string=client_version_string; + kex->server_version_string=server_version_string; + kex->verify_host_key=&verify_host_key_callback; + ++#ifdef GSSAPI ++ kex->gss_deleg_creds = options.gss_deleg_creds; ++ kex->gss_trust_dns = options.gss_trust_dns; ++ kex->gss_host = gss_host; ++#endif ++ + xxx_kex = kex; + + dispatch_run(DISPATCH_BLOCK, &kex->done, kex); +@@ -223,6 +269,7 @@ + void input_gssapi_hash(int type, u_int32_t, void *); + void input_gssapi_error(int, u_int32_t, void *); + void input_gssapi_errtok(int, u_int32_t, void *); ++int userauth_gsskeyex(Authctxt *authctxt); + #endif + + void userauth(Authctxt *, char *); +@@ -238,6 +285,10 @@ + + Authmethod authmethods[] = { + #ifdef GSSAPI ++ {"gssapi-keyex", ++ userauth_gsskeyex, ++ &options.gss_authentication, ++ NULL}, + {"gssapi-with-mic", + userauth_gssapi, + &options.gss_authentication, +@@ -500,6 +551,12 @@ + static u_int mech = 0; + OM_uint32 min; + int ok = 0; ++ char *gss_host = NULL; ++ ++ if (options.gss_trust_dns) ++ gss_host = (char *)get_canonical_hostname(1); ++ else ++ gss_host = (char *)authctxt->host; + + /* Try one GSSAPI method at a time, rather than sending them all at + * once. */ +@@ -512,7 +569,7 @@ + /* My DER encoding requires length<128 */ + if (gss_supported->elements[mech].length < 128 && + ssh_gssapi_check_mechanism(&gssctxt, +- &gss_supported->elements[mech], authctxt->host)) { ++ &gss_supported->elements[mech], gss_host)) { + ok = 1; /* Mechanism works */ + } else { + mech++; +@@ -608,8 +665,8 @@ + { + Authctxt *authctxt = ctxt; + Gssctxt *gssctxt; +- int oidlen; +- char *oidv; ++ u_int oidlen; ++ u_char *oidv; + + if (authctxt == NULL) + fatal("input_gssapi_response: no authentication context"); +@@ -716,6 +773,48 @@ + xfree(msg); + xfree(lang); + } ++ ++int ++userauth_gsskeyex(Authctxt *authctxt) ++{ ++ Buffer b; ++ gss_buffer_desc gssbuf; ++ gss_buffer_desc mic = GSS_C_EMPTY_BUFFER; ++ OM_uint32 ms; ++ ++ static int attempt = 0; ++ if (attempt++ >= 1) ++ return (0); ++ ++ if (gss_kex_context == NULL) { ++ debug("No valid Key exchange context"); ++ return (0); ++ } ++ ++ ssh_gssapi_buildmic(&b, authctxt->server_user, authctxt->service, ++ "gssapi-keyex"); ++ ++ gssbuf.value = buffer_ptr(&b); ++ gssbuf.length = buffer_len(&b); ++ ++ if (GSS_ERROR(ssh_gssapi_sign(gss_kex_context, &gssbuf, &mic))) { ++ buffer_free(&b); ++ return (0); ++ } ++ ++ packet_start(SSH2_MSG_USERAUTH_REQUEST); ++ packet_put_cstring(authctxt->server_user); ++ packet_put_cstring(authctxt->service); ++ packet_put_cstring(authctxt->method->name); ++ packet_put_string(mic.value, mic.length); ++ packet_send(); ++ ++ buffer_free(&b); ++ gss_release_buffer(&ms, &mic); ++ ++ return (1); ++} ++ + #endif /* GSSAPI */ + + int +Index: sshd.c +=================================================================== +RCS file: /cvs/openssh/sshd.c,v +retrieving revision 1.361 +diff -u -r1.361 sshd.c +--- sshd.c 7 Nov 2006 12:14:42 -0000 1.361 ++++ sshd.c 20 Dec 2006 10:41:46 -0000 +@@ -117,6 +117,10 @@ + #include "monitor_fdpass.h" + #include "version.h" + ++#ifdef USE_SECURITY_SESSION_API ++#include <Security/AuthSession.h> ++#endif ++ + #ifdef LIBWRAP + #include <tcpd.h> + #include <syslog.h> +@@ -1476,10 +1480,13 @@ + logit("Disabling protocol version 1. Could not load host key"); + options.protocol &= ~SSH_PROTO_1; + } ++#ifndef GSSAPI ++ /* The GSSAPI key exchange can run without a host key */ + if ((options.protocol & SSH_PROTO_2) && !sensitive_data.have_ssh2_key) { + logit("Disabling protocol version 2. Could not load host key"); + options.protocol &= ~SSH_PROTO_2; + } ++#endif + if (!(options.protocol & (SSH_PROTO_1|SSH_PROTO_2))) { + logit("sshd: no hostkeys available -- exiting."); + exit(1); +@@ -1754,6 +1761,60 @@ + /* Log the connection. */ + verbose("Connection from %.500s port %d", remote_ip, remote_port); + ++#ifdef USE_SECURITY_SESSION_API ++ /* ++ * Create a new security session for use by the new user login if ++ * the current session is the root session or we are not launched ++ * by inetd (eg: debugging mode or server mode). We do not ++ * necessarily need to create a session if we are launched from ++ * inetd because Panther xinetd will create a session for us. ++ * ++ * The only case where this logic will fail is if there is an ++ * inetd running in a non-root session which is not creating ++ * new sessions for us. Then all the users will end up in the ++ * same session (bad). ++ * ++ * When the client exits, the session will be destroyed for us ++ * automatically. ++ * ++ * We must create the session before any credentials are stored ++ * (including AFS pags, which happens a few lines below). ++ */ ++ { ++ OSStatus err = 0; ++ SecuritySessionId sid = 0; ++ SessionAttributeBits sattrs = 0; ++ ++ err = SessionGetInfo(callerSecuritySession, &sid, &sattrs); ++ if (err) ++ error("SessionGetInfo() failed with error %.8X", ++ (unsigned) err); ++ else ++ debug("Current Session ID is %.8X / Session Attributes are %.8X", ++ (unsigned) sid, (unsigned) sattrs); ++ ++ if (inetd_flag && !(sattrs & sessionIsRoot)) ++ debug("Running in inetd mode in a non-root session... " ++ "assuming inetd created the session for us."); ++ else { ++ debug("Creating new security session..."); ++ err = SessionCreate(0, sessionHasTTY | sessionIsRemote); ++ if (err) ++ error("SessionCreate() failed with error %.8X", ++ (unsigned) err); ++ ++ err = SessionGetInfo(callerSecuritySession, &sid, ++ &sattrs); ++ if (err) ++ error("SessionGetInfo() failed with error %.8X", ++ (unsigned) err); ++ else ++ debug("New Session ID is %.8X / Session Attributes are %.8X", ++ (unsigned) sid, (unsigned) sattrs); ++ } ++ } ++#endif ++ + /* + * We don't want to listen forever unless the other side + * successfully authenticates itself. So we set up an alarm which is +@@ -2112,12 +2173,59 @@ + + myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = list_hostkey_types(); + ++#ifdef GSSAPI ++ { ++ char *orig; ++ char *gss = NULL; ++ char *newstr = NULL; ++ orig = myproposal[PROPOSAL_KEX_ALGS]; ++ ++ /* ++ * If we don't have a host key, then there's no point advertising ++ * the other key exchange algorithms ++ */ ++ ++ if (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]) == 0) ++ orig = NULL; ++ ++ if (options.gss_keyex) ++ gss = ssh_gssapi_server_mechanisms(); ++ else ++ gss = NULL; ++ ++ if (gss && orig) ++ xasprintf(&newstr, "%s,%s", gss, orig); ++ else if (gss) ++ newstr = gss; ++ else if (orig) ++ newstr = orig; ++ ++ /* ++ * If we've got GSSAPI mechanisms, then we've got the 'null' host ++ * key alg, but we can't tell people about it unless its the only ++ * host key algorithm we support ++ */ ++ if (gss && (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS])) == 0) ++ myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = "null"; ++ ++ if (newstr) ++ myproposal[PROPOSAL_KEX_ALGS] = newstr; ++ else ++ fatal("No supported key exchange algorithms"); ++ } ++#endif ++ + /* start key exchange */ + kex = kex_setup(myproposal); + kex->kex[KEX_DH_GRP1_SHA1] = kexdh_server; + kex->kex[KEX_DH_GRP14_SHA1] = kexdh_server; + kex->kex[KEX_DH_GEX_SHA1] = kexgex_server; + kex->kex[KEX_DH_GEX_SHA256] = kexgex_server; ++#ifdef GSSAPI ++ kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server; ++ kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server; ++ kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server; ++#endif + kex->server = 1; + kex->client_version_string=client_version_string; + kex->server_version_string=server_version_string; +Index: sshd_config +=================================================================== +RCS file: /cvs/openssh/sshd_config,v +retrieving revision 1.76 +diff -u -r1.76 sshd_config +--- sshd_config 24 Jul 2006 04:06:47 -0000 1.76 ++++ sshd_config 20 Dec 2006 10:41:46 -0000 +@@ -68,6 +68,8 @@ + # GSSAPI options + #GSSAPIAuthentication no + #GSSAPICleanupCredentials yes ++#GSSAPIStrictAcceptorCheck yes ++#GSSAPIKeyExchange no + + # Set this to 'yes' to enable PAM authentication, account processing, + # and session processing. If this is enabled, PAM authentication will +Index: sshd_config.5 +=================================================================== +RCS file: /cvs/openssh/sshd_config.5,v +retrieving revision 1.76 +diff -u -r1.76 sshd_config.5 +--- sshd_config.5 30 Aug 2006 01:06:34 -0000 1.76 ++++ sshd_config.5 20 Dec 2006 10:41:46 -0000 +@@ -320,12 +320,35 @@ + The default is + .Dq no . + Note that this option applies to protocol version 2 only. ++.It Cm GSSAPIKeyExchange ++Specifies whether key exchange based on GSSAPI is allowed. GSSAPI key exchange ++doesn't rely on ssh keys to verify host identity. ++The default is ++.Dq no . ++Note that this option applies to protocol version 2 only. + .It Cm GSSAPICleanupCredentials + Specifies whether to automatically destroy the user's credentials cache + on logout. + The default is + .Dq yes . + Note that this option applies to protocol version 2 only. ++.It Cm GSSAPIStrictAcceptorCheck ++Determines whether to be strict about the identity of the GSSAPI acceptor ++a client authenticates against. If ++.Dq yes ++then the client must authenticate against the ++.Pa host ++service on the current hostname. If ++.Dq no ++then the client may authenticate against any service key stored in the ++machine's default store. This facility is provided to assist with operation ++on multi homed machines. ++The default is ++.Dq yes . ++Note that this option applies only to protocol version 2 GSSAPI connections, ++and setting it to ++.Dq no ++may only work with recent Kerberos GSSAPI libraries. + .It Cm HostbasedAuthentication + Specifies whether rhosts or /etc/hosts.equiv authentication together + with successful public key client host authentication is allowed diff --git a/security/openssh-portable/files/openssh-lpk.patch b/security/openssh-portable/files/openssh-lpk.patch deleted file mode 100644 index 19c747d31fe6..000000000000 --- a/security/openssh-portable/files/openssh-lpk.patch +++ /dev/null @@ -1,1813 +0,0 @@ -# -# Based on 4.3p1-0.3.7 patch from http://www.opendarwin.org/en/projects/openssh-lpk/ -# ---- Makefile.in.orig Tue Sep 12 08:54:10 2006 -+++ Makefile.in Sat Oct 7 17:27:42 2006 -@@ -88,7 +88,7 @@ - auth-krb5.o \ - auth2-gss.o gss-serv.o gss-serv-krb5.o \ - loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o \ -- audit.o audit-bsm.o platform.o -+ audit.o audit-bsm.o platform.o ldapauth.o - - MANPAGES = 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-rand-helper.8.out ssh-keysign.8.out sshd_config.5.out ssh_config.5.out - MANPAGES_IN = scp.1 ssh-add.1 ssh-agent.1 ssh-keygen.1 ssh-keyscan.1 ssh.1 sshd.8 sftp-server.8 sftp.1 ssh-rand-helper.8 ssh-keysign.8 sshd_config.5 ssh_config.5 ---- README.lpk.orig Sat Oct 7 17:26:32 2006 -+++ README.lpk Sat Oct 7 17:26:32 2006 -@@ -0,0 +1,265 @@ -+OpenSSH LDAP PUBLIC KEY PATCH -+Copyright (c) 2003 Eric AUGE (eau@phear.org) -+All rights reserved. -+ -+Redistribution and use in source and binary forms, with or without -+modification, are permitted provided that the following conditions -+are met: -+1. Redistributions of source code must retain the above copyright -+ notice, this list of conditions and the following disclaimer. -+2. Redistributions in binary form must reproduce the above copyright -+ notice, this list of conditions and the following disclaimer in the -+ documentation and/or other materials provided with the distribution. -+3. The name of the author may not be used to endorse or promote products -+ derived from this software without specific prior written permission. -+ -+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -+IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ -+purposes of this patch: -+ -+This patch would help to have authentication centralization policy -+using ssh public key authentication. -+This patch could be an alternative to other "secure" authentication system -+working in a similar way (Kerberos, SecurID, etc...), except the fact -+that it's based on OpenSSH and its public key abilities. -+ -+>> FYI: << -+'uid': means unix accounts existing on the current server -+'lpkServerGroup:' mean server group configured on the current server ('lpkServerGroup' in sshd_config) -+ -+example schema: -+ -+ -+ server1 (uid: eau,rival,toto) (lpkServerGroup: unix) -+ ___________ / -+ / \ --- - server3 (uid: eau, titi) (lpkServerGroup: unix) -+ | LDAP Server | \ -+ | eau ,rival | server2 (uid: rival, eau) (lpkServerGroup: unix) -+ | titi ,toto | -+ | userx,.... | server5 (uid: eau) (lpkServerGroup: mail) -+ \___________/ \ / -+ ----- - server4 (uid: eau, rival) (no group configured) -+ \ -+ etc... -+ -+- WHAT WE NEED : -+ -+ * configured LDAP server somewhere on the network (i.e. OpenLDAP) -+ * patched sshd (with this patch ;) -+ * LDAP user(/group) entry (look at users.ldif (& groups.ldif)): -+ User entry: -+ - attached to the 'ldapPublicKey' objectclass -+ - attached to the 'posixAccount' objectclass -+ - with a filled 'sshPublicKey' attribute -+ Example: -+ dn: uid=eau,ou=users,dc=cuckoos,dc=net -+ objectclass: top -+ objectclass: person -+ objectclass: organizationalPerson -+ objectclass: posixAccount -+ objectclass: ldapPublicKey -+ description: Eric AUGE Account -+ userPassword: blah -+ cn: Eric AUGE -+ sn: Eric AUGE -+ uid: eau -+ uidNumber: 1034 -+ gidNumber: 1 -+ homeDirectory: /export/home/eau -+ sshPublicKey: ssh-dss AAAAB3... -+ sshPublicKey: ssh-dss AAAAM5... -+ -+ Group entry: -+ - attached to the 'posixGroup' objectclass -+ - with a 'cn' groupname attribute -+ - with multiple 'memberUid' attributes filled with usernames allowed in this group -+ Example: -+ # few members -+ dn: cn=unix,ou=groups,dc=cuckoos,dc=net -+ objectclass: top -+ objectclass: posixGroup -+ description: Unix based servers group -+ cn: unix -+ gidNumber: 1002 -+ memberUid: eau -+ memberUid: user1 -+ memberUid: user2 -+ -+ -+- HOW IT WORKS : -+ -+ * without patch -+ If a user wants to authenticate to log in a server the sshd, will first look for authentication method allowed (RSAauth,kerberos,etc..) -+ and if RSAauth and tickets based auth fails, it will fallback to standard password authentication (if enabled). -+ -+ * with the patch -+ If a user want to authenticate to log in a server, the sshd will first look for auth method including LDAP pubkey, if the ldappubkey options is enabled. -+ It will do an ldapsearch to get the public key directly from the LDAP instead of reading it from the server filesystem. -+ (usually in $HOME/.ssh/authorized_keys) -+ -+ If groups are enabled, it will also check if the user that wants to login is in the group of the server he is trying to log into. -+ If it fails, it falls back on RSA auth files ($HOME/.ssh/authorized_keys), etc.. and finally to standard password authentication (if enabled). -+ -+ 7 tokens are added to sshd_config : -+ # here is the new patched ldap related tokens -+ # entries in your LDAP must be posixAccount & strongAuthenticationUser & posixGroup -+ UseLPK yes # look the pub key into LDAP -+ LpkServers ldap://10.31.32.5/ ldap://10.31.32.4 ldap://10.31.32.3 # which LDAP server for users ? (URL format) -+ LpkUserDN ou=users,dc=foobar,dc=net # which base DN for users ? -+ LpkGroupDN ou=groups,dc=foobar,dc=net # which base DN for groups ? -+ LpkBindDN cn=manager,dc=foobar,dc=net # which bind DN ? -+ LpkBindPw asecret # bind DN credidentials -+ LpkServerGroup agroupname # the group the server is part of -+ -+ Right now i'm using anonymous binding to get public keys, because getting public keys of someone doesn't impersonate him¸ but there is some -+ flaws you have to take care of. -+ -+- HOW TO INSERT A USER/KEY INTO AN LDAP ENTRY -+ -+ * my way (there is plenty :) -+ - create ldif file (i.e. users.ldif) -+ - cat ~/.ssh/id_dsa.pub OR cat ~/.ssh/id_rsa.pub OR cat ~/.ssh/identity.pub -+ - my way in 4 steps : -+ Example: -+ -+ # you add this to the user entry in the LDIF file : -+ [...] -+ objectclass: posixAccount -+ objectclass: ldapPublicKey -+ [...] -+ sshPubliKey: ssh-dss AAAABDh12DDUR2... -+ [...] -+ -+ # insert your entry and you're done :) -+ ldapadd -D balblabla -w bleh < file.ldif -+ -+ all standard options can be present in the 'sshPublicKey' attribute. -+ -+- WHY : -+ -+ Simply because, i was looking for a way to centralize all sysadmins authentication, easily, without completely using LDAP -+ as authentication method (like pam_ldap etc..). -+ -+ After looking into Kerberos, SecurID, and other centralized secure authentications systems, the use of RSA and LDAP to get -+ public key for authentication allows us to control who has access to which server (the user needs an account and to be in 'strongAuthenticationUser' -+ objectclass within LDAP and part of the group the SSH server is in). -+ -+ Passwords update are no longer a nightmare for a server farm (key pair passphrase is stored on each user's box and private key is locally encrypted using his passphrase -+ so each user can change it as much as he wants). -+ -+ Blocking a user account can be done directly from the LDAP (if sshd is using RSAAuth + ldap only). -+ -+- RULES : -+ Entry in the LDAP server must respect 'posixAccount' and 'ldapPublicKey' which are defined in core.schema. -+ and the additionnal lpk.schema. -+ -+ This patch could allow a smooth transition between standard auth (/etc/passwd) and complete LDAP based authentication -+ (pamldap, nss_ldap, etc..). -+ -+ This can be an alternative to other (old?/expensive?) authentication methods (Kerberos/SecurID/..). -+ -+ Referring to schema at the beginning of this file if user 'eau' is only in group 'unix' -+ 'eau' would ONLY access 'server1', 'server2', 'server3' AND 'server4' BUT NOT 'server5'. -+ If you then modify the LDAP 'mail' group entry to add 'memberUid: eau' THEN user 'eau' would be able -+ to log in 'server5' (i hope you got the idea, my english is bad :). -+ -+ Each server's sshd is patched and configured to ask the public key and the group infos in the LDAP -+ server. -+ When you want to allow a new user to have access to the server parc, you just add him an account on -+ your servers, you add his public key into his entry on the LDAP server, it's done. -+ -+ Because sshds are looking public keys into the LDAP directly instead of a file ($HOME/.ssh/authorized_keys). -+ -+ When the user needs to change his passphrase he can do it directly from his workstation by changing -+ his own key set lock passphrase, and all servers are automatically aware. -+ -+ With a CAREFUL LDAP server configuration you could allow a user to add/delete/modify his own entry himself -+ so he can add/modify/delete himself his public key when needed. -+ -+ FLAWS : -+ LDAP must be well configured, getting the public key of some user is not a problem, but if anonymous LDAP -+ allow write to users dn, somebody could replace someuser's public key by its own and impersonate some -+ of your users in all your server farm be VERY CAREFUL. -+ -+ MITM attack when sshd is requesting the public key, could lead to a compromise of your servers allowing login -+ as the impersonnated user. -+ -+ If LDAP server is down then, fallback on passwd auth. -+ -+ the ldap code part has not been well audited yet. -+ -+- LDAP USER ENTRY EXAMPLES (LDIF Format, look in users.ldif) -+ --- CUT HERE --- -+ dn: uid=jdoe,ou=users,dc=foobar,dc=net -+ objectclass: top -+ objectclass: person -+ objectclass: organizationalPerson -+ objectclass: posixAccount -+ objectclass: ldapPublicKey -+ description: My account -+ cn: John Doe -+ sn: John Doe -+ uid: jdoe -+ uidNumber: 100 -+ gidNumber: 100 -+ homeDirectory: /home/jdoe -+ sshPublicKey: ssh-dss AAAAB3NzaC1kc3MAAAEBAOvL8pREUg9wSy/8+hQJ54YF3AXkB0OZrXB.... -+ [...] -+ --- CUT HERE --- -+ -+- LDAP GROUP ENTRY EXAMPLES (LDIF Format, look in groups.ldif) -+ --- CUT HERE --- -+ dn: cn=unix,ou=groups,dc=cuckoos,dc=net -+ objectclass: top -+ objectclass: posixGroup -+ description: Unix based servers group -+ cn: unix -+ gidNumber: 1002 -+ memberUid: jdoe -+ memberUid: user1 -+ memberUid: user2 -+ [...] -+ --- CUT HERE --- -+ -+>> FYI: << -+Multiple 'sshPublicKey' in a user entry are allowed, as well as multiple 'memberUid' attributes in a group entry -+ -+- COMPILING: -+ 1. Apply the patch -+ 2. ./configure --with-your-options --with-ldap=/prefix/to/ldap_libs_and_includes -+ 3. make -+ 4. it's done. -+ -+- BLA : -+ I hope this could help, and i hope to be clear enough,, or give ideas. questions/comments/improvements are welcome. -+ -+- TODO : -+ Redesign differently. -+ -+- DOCS/LINK : -+ http://pacsec.jp/core05/psj05-barisani-en.pdf -+ http://fritz.potsdam.edu/projects/openssh-lpk/ -+ http://fritz.potsdam.edu/projects/sshgate/ -+ http://www.opendarwin.org/projects/openssh-lpk/files/examples/lpk-usrdoc.txt -+ http://lam.sf.net/ ( http://lam.sourceforge.net/documentation/supportedSchemas.htm ) -+ -+- CONTRIBUTORS/IDEAS/GREETS : -+ - Falk Siemonsmeier -> 3.7 patch port candidate -+ - Jacob Rief -> ideas (group && cleanups) -+ - Michael.Durchgraf@dregis.com -> Bugfixes thanks ;) -+ - frederic.peters@free.fr -> X509 keys LDAP patch (old) -+ - oink -> bugfixes -+ - finlay dobbie -> new fresh start with this guy :) -+ -+- CONTACT : -+ - Eric AUGE <eau@phear.org>, <eau@opendarwin.org> -+ - Andrea Barisani <lcars@opendarwin.org> ---- auth-rsa.c.orig Fri Sep 1 02:38:36 2006 -+++ auth-rsa.c Sat Oct 7 17:26:32 2006 -@@ -173,10 +173,96 @@ - u_long linenum = 0; - struct stat st; - Key *key; -+#ifdef WITH_LDAP_PUBKEY -+ ldap_key_t * k; -+ unsigned int i = 0; -+#endif - - /* Temporarily use the user's uid. */ - temporarily_use_uid(pw); - -+#ifdef WITH_LDAP_PUBKEY -+ /* here is the job */ -+ key = key_new(KEY_RSA1); -+ -+ if (options.lpk.on) { -+ debug("[LDAP] trying LDAP first uid=%s", pw->pw_name); -+ if ( ldap_ismember(&options.lpk, pw->pw_name) > 0) { -+ if ( (k = ldap_getuserkey(&options.lpk, pw->pw_name)) != NULL) { -+ for (i = 0 ; i < k->num ; i++) { -+ char *cp, *options = NULL; -+ -+ for (cp = k->keys[i]->bv_val; *cp == ' ' || *cp == '\t'; cp++) -+ ; -+ if (!*cp || *cp == '\n' || *cp == '#') -+ continue; -+ -+ /* -+ * Check if there are options for this key, and if so, -+ * save their starting address and skip the option part -+ * for now. If there are no options, set the starting -+ * address to NULL. -+ */ -+ if (*cp < '0' || *cp > '9') { -+ int quoted = 0; -+ options = cp; -+ for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) { -+ if (*cp == '\\' && cp[1] == '"') -+ cp++; /* Skip both */ -+ else if (*cp == '"') -+ quoted = !quoted; -+ } -+ } else -+ options = NULL; -+ -+ /* Parse the key from the line. */ -+ if (hostfile_read_key(&cp, &bits, key) == 0) { -+ debug("[LDAP] line %d: non ssh1 key syntax", i); -+ continue; -+ } -+ /* cp now points to the comment part. */ -+ -+ /* Check if the we have found the desired key (identified by its modulus). */ -+ if (BN_cmp(key->rsa->n, client_n) != 0) -+ continue; -+ -+ /* check the real bits */ -+ if (bits != (unsigned int)BN_num_bits(key->rsa->n)) -+ logit("[LDAP] Warning: ldap, line %lu: keysize mismatch: " -+ "actual %d vs. announced %d.", (unsigned long)i, BN_num_bits(key->rsa->n), bits); -+ -+ /* We have found the desired key. */ -+ /* -+ * If our options do not allow this key to be used, -+ * do not send challenge. -+ */ -+ if (!auth_parse_options(pw, options, "[LDAP]", (unsigned long) i)) -+ continue; -+ -+ /* break out, this key is allowed */ -+ allowed = 1; -+ -+ /* add the return stuff etc... */ -+ /* Restore the privileged uid. */ -+ restore_uid(); -+ -+ /* return key if allowed */ -+ if (allowed && rkey != NULL) -+ *rkey = key; -+ else -+ key_free(key); -+ -+ ldap_keys_free(k); -+ return (allowed); -+ } -+ } else { -+ logit("[LDAP] no keys found for '%s'!", pw->pw_name); -+ } -+ } else { -+ logit("[LDAP] '%s' is not in '%s'", pw->pw_name, options.lpk.sgroup); -+ } -+ } -+#endif - /* The authorized keys. */ - file = authorized_keys_file(pw); - debug("trying public RSA key file %s", file); ---- auth2-pubkey.c.orig Fri Aug 4 23:39:39 2006 -+++ auth2-pubkey.c Sat Oct 7 17:26:32 2006 -@@ -53,6 +53,10 @@ - #include "monitor_wrap.h" - #include "misc.h" - -+#ifdef WITH_LDAP_PUBKEY -+#include "ldapauth.h" -+#endif -+ - /* import */ - extern ServerOptions options; - extern u_char *session_id2; -@@ -186,10 +190,79 @@ - struct stat st; - Key *found; - char *fp; -+#ifdef WITH_LDAP_PUBKEY -+ ldap_key_t * k; -+ unsigned int i = 0; -+#endif - - /* Temporarily use the user's uid. */ - temporarily_use_uid(pw); - -+#ifdef WITH_LDAP_PUBKEY -+ found_key = 0; -+ /* allocate a new key type */ -+ found = key_new(key->type); -+ -+ /* first check if the options is enabled, then try.. */ -+ if (options.lpk.on) { -+ debug("[LDAP] trying LDAP first uid=%s",pw->pw_name); -+ if (ldap_ismember(&options.lpk, pw->pw_name) > 0) { -+ if ((k = ldap_getuserkey(&options.lpk, pw->pw_name)) != NULL) { -+ /* Skip leading whitespace, empty and comment lines. */ -+ for (i = 0 ; i < k->num ; i++) { -+ /* dont forget if multiple keys to reset options */ -+ char *cp, *options = NULL; -+ -+ for (cp = (char *)k->keys[i]->bv_val; *cp == ' ' || *cp == '\t'; cp++) -+ ; -+ if (!*cp || *cp == '\n' || *cp == '#') -+ continue; -+ -+ if (key_read(found, &cp) != 1) { -+ /* no key? check if there are options for this key */ -+ int quoted = 0; -+ debug2("[LDAP] user_key_allowed: check options: '%s'", cp); -+ options = cp; -+ for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) { -+ if (*cp == '\\' && cp[1] == '"') -+ cp++; /* Skip both */ -+ else if (*cp == '"') -+ quoted = !quoted; -+ } -+ /* Skip remaining whitespace. */ -+ for (; *cp == ' ' || *cp == '\t'; cp++) -+ ; -+ if (key_read(found, &cp) != 1) { -+ debug2("[LDAP] user_key_allowed: advance: '%s'", cp); -+ /* still no key? advance to next line*/ -+ continue; -+ } -+ } -+ -+ if (key_equal(found, key) && -+ auth_parse_options(pw, options, file, linenum) == 1) { -+ found_key = 1; -+ debug("[LDAP] matching key found"); -+ fp = key_fingerprint(found, SSH_FP_MD5, SSH_FP_HEX); -+ verbose("[LDAP] Found matching %s key: %s", key_type(found), fp); -+ -+ /* restoring memory */ -+ ldap_keys_free(k); -+ xfree(fp); -+ restore_uid(); -+ key_free(found); -+ return found_key; -+ break; -+ } -+ }/* end of LDAP for() */ -+ } else { -+ logit("[LDAP] no keys found for '%s'!", pw->pw_name); -+ } -+ } else { -+ logit("[LDAP] '%s' is not in '%s'", pw->pw_name, options.lpk.sgroup); -+ } -+ } -+#endif - debug("trying public key file %s", file); - - /* Fail quietly if file does not exist */ ---- config.h.in.orig Tue Sep 26 08:03:33 2006 -+++ config.h.in Sat Oct 7 17:26:32 2006 -@@ -510,6 +510,9 @@ - /* Define to 1 if you have the <linux/if_tun.h> header file. */ - #undef HAVE_LINUX_IF_TUN_H - -+/* Define if you want LDAP support */ -+#undef WITH_LDAP_PUBKEY -+ - /* Define if your libraries define login() */ - #undef HAVE_LOGIN - ---- configure.orig Tue Sep 26 08:03:41 2006 -+++ configure Sat Oct 7 17:28:35 2006 -@@ -1327,6 +1327,7 @@ - --with-audit=module Enable EXPERIMENTAL audit support (modules=debug,bsm) - --with-ssl-dir=PATH Specify path to OpenSSL installation - --with-ssl-engine Enable OpenSSL (hardware) ENGINE support -+ --with-ldap[=PATH] Enable LDAP support (optionally in PATH) - --with-pam Enable PAM support - --with-rand-helper Use subprocess to gather strong randomness - --with-prngd-port=PORT read entropy from PRNGD/EGD TCP localhost:PORT -@@ -11267,6 +11268,88 @@ - echo "$as_me: failed program was:" >&5 - sed 's/^/| /' conftest.$ac_ext >&5 - -+# Check whether user wants LDAP support -+LDAP_MSG="no" -+ -+# Check whether --with-ldap or --without-ldap was given. -+if test "${with_ldap+set}" = set; then -+ withval="$with_ldap" -+ -+ if test "x$withval" != "xno" ; then -+ -+ if test "x$withval" != "xyes" ; then -+ CPPFLAGS="$CPPFLAGS -I${withval}/include" -+ LDFLAGS="$LDFLAGS -L${withval}/lib" -+ fi -+ -+ cat >>confdefs.h <<\_ACEOF -+#define WITH_LDAP_PUBKEY 1 -+_ACEOF -+ -+ LIBS="-lldap $LIBS" -+ LDAP_MSG="yes" -+ -+ echo "$as_me:$LINENO: checking for LDAP support" >&5 -+echo $ECHO_N "checking for LDAP support... $ECHO_C" >&6 -+ cat >conftest.$ac_ext <<_ACEOF -+/* confdefs.h. */ -+_ACEOF -+cat confdefs.h >>conftest.$ac_ext -+cat >>conftest.$ac_ext <<_ACEOF -+/* end confdefs.h. */ -+#include <sys/types.h> -+ #include <ldap.h> -+int -+main () -+{ -+(void)ldap_init(0, 0); -+ ; -+ return 0; -+} -+_ACEOF -+rm -f conftest.$ac_objext -+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 -+ (eval $ac_compile) 2>conftest.er1 -+ ac_status=$? -+ grep -v '^ *+' conftest.er1 >conftest.err -+ rm -f conftest.er1 -+ cat conftest.err >&5 -+ echo "$as_me:$LINENO: \$? = $ac_status" >&5 -+ (exit $ac_status); } && -+ { ac_try='test -z "$ac_c_werror_flag" -+ || test ! -s conftest.err' -+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 -+ (eval $ac_try) 2>&5 -+ ac_status=$? -+ echo "$as_me:$LINENO: \$? = $ac_status" >&5 -+ (exit $ac_status); }; } && -+ { ac_try='test -s conftest.$ac_objext' -+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 -+ (eval $ac_try) 2>&5 -+ ac_status=$? -+ echo "$as_me:$LINENO: \$? = $ac_status" >&5 -+ (exit $ac_status); }; }; then -+ echo "$as_me:$LINENO: result: yes" >&5 -+echo "${ECHO_T}yes" >&6 -+else -+ echo "$as_me: failed program was:" >&5 -+sed 's/^/| /' conftest.$ac_ext >&5 -+ -+ -+ echo "$as_me:$LINENO: result: no" >&5 -+echo "${ECHO_T}no" >&6 -+ { { echo "$as_me:$LINENO: error: ** Incomplete or missing ldap libraries **" >&5 -+echo "$as_me: error: ** Incomplete or missing ldap libraries **" >&2;} -+ { (exit 1); exit 1; }; } -+ -+ -+fi -+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext -+ fi -+ -+ -+fi; -+ - - { { echo "$as_me:$LINENO: error: *** zlib missing - please install first or check config.log ***" >&5 - echo "$as_me: error: *** zlib missing - please install first or check config.log ***" >&2;} -@@ -33408,6 +33491,7 @@ - echo " Smartcard support: $SCARD_MSG" - echo " S/KEY support: $SKEY_MSG" - echo " TCP Wrappers support: $TCPW_MSG" -+echo " LDAP support: $LDAP_MSG" - echo " MD5 password support: $MD5_MSG" - echo " libedit support: $LIBEDIT_MSG" - echo " Solaris process contract support: $SPC_MSG" ---- configure.ac.orig Sun Sep 24 16:08:59 2006 -+++ configure.ac Sat Oct 7 17:26:32 2006 -@@ -1218,6 +1218,37 @@ - esac ] - ) - -+# Check whether user wants LDAP support -+LDAP_MSG="no" -+AC_ARG_WITH(ldap, -+ [ --with-ldap[[=PATH]] Enable LDAP support (optionally in PATH)], -+ [ -+ if test "x$withval" != "xno" ; then -+ -+ if test "x$withval" != "xyes" ; then -+ CPPFLAGS="$CPPFLAGS -I${withval}/include" -+ LDFLAGS="$LDFLAGS -L${withval}/lib" -+ fi -+ -+ AC_DEFINE(WITH_LDAP_PUBKEY) -+ LIBS="-lldap $LIBS" -+ LDAP_MSG="yes" -+ -+ AC_MSG_CHECKING([for LDAP support]) -+ AC_TRY_COMPILE( -+ [#include <sys/types.h> -+ #include <ldap.h>], -+ [(void)ldap_init(0, 0);], -+ [AC_MSG_RESULT(yes)], -+ [ -+ AC_MSG_RESULT(no) -+ AC_MSG_ERROR([** Incomplete or missing ldap libraries **]) -+ ] -+ ) -+ fi -+ ] -+) -+ - dnl Checks for library functions. Please keep in alphabetical order - AC_CHECK_FUNCS( \ - arc4random \ -@@ -3966,6 +3997,7 @@ - echo " Smartcard support: $SCARD_MSG" - echo " S/KEY support: $SKEY_MSG" - echo " TCP Wrappers support: $TCPW_MSG" -+echo " LDAP support: $LDAP_MSG" - echo " MD5 password support: $MD5_MSG" - echo " libedit support: $LIBEDIT_MSG" - echo " Solaris process contract support: $SPC_MSG" ---- ldapauth.c.orig Sat Oct 7 17:26:32 2006 -+++ ldapauth.c Sat Oct 7 17:26:32 2006 -@@ -0,0 +1,547 @@ -+/* -+ * $Id: openssh-lpk-4.3p1-0.3.7.patch,v 1.3 2006/04/18 15:29:09 eau Exp $ -+ */ -+ -+/* -+ * -+ * Copyright (c) 2005, Eric AUGE <eau@phear.org> -+ * All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: -+ * -+ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. -+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -+ * Neither the name of the phear.org nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, -+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -+ * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, -+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ * -+ * -+ */ -+ -+#include "includes.h" -+ -+#ifdef WITH_LDAP_PUBKEY -+ -+#include <stdio.h> -+#include <stdlib.h> -+#include <unistd.h> -+#include <string.h> -+ -+#include "ldapauth.h" -+#include "log.h" -+ -+static char *attrs[] = { -+ PUBKEYATTR, -+ NULL -+}; -+ -+/* filter building infos */ -+#define FILTER_GROUP_PREFIX "(&(objectclass=posixGroup)" -+#define FILTER_OR_PREFIX "(|" -+#define FILTER_OR_SUFFIX ")" -+#define FILTER_CN_PREFIX "(cn=" -+#define FILTER_CN_SUFFIX ")" -+#define FILTER_UID_FORMAT "(memberUid=%s)" -+#define FILTER_GROUP_SUFFIX ")" -+#define FILTER_GROUP_SIZE(group) (size_t) (strlen(group)+(ldap_count_group(group)*5)+52) -+ -+/* just filter building stuff */ -+#define REQUEST_GROUP_SIZE(filter, uid) (size_t) (strlen(filter)+strlen(uid)+1) -+#define REQUEST_GROUP(buffer, prefilter, pwname) \ -+ buffer = (char *) calloc(REQUEST_GROUP_SIZE(prefilter, pwname), sizeof(char)); \ -+ if (!buffer) { \ -+ perror("calloc()"); \ -+ return FAILURE; \ -+ } \ -+ snprintf(buffer, REQUEST_GROUP_SIZE(prefilter,pwname), prefilter, pwname) -+/* -+XXX OLD group building macros -+#define REQUEST_GROUP_SIZE(grp, uid) (size_t) (strlen(grp)+strlen(uid)+46) -+#define REQUEST_GROUP(buffer,pwname,grp) \ -+ buffer = (char *) calloc(REQUEST_GROUP_SIZE(grp, pwname), sizeof(char)); \ -+ if (!buffer) { \ -+ perror("calloc()"); \ -+ return FAILURE; \ -+ } \ -+ snprintf(buffer,REQUEST_GROUP_SIZE(grp,pwname),"(&(objectclass=posixGroup)(cn=%s)(memberUid=%s))",grp,pwname) -+ */ -+ -+#define REQUEST_USER_SIZE(uid) (size_t) (strlen(uid)+64) -+#define REQUEST_USER(buffer, pwname) \ -+ buffer = (char *) calloc(REQUEST_USER_SIZE(pwname), sizeof(char)); \ -+ if (!buffer) { \ -+ perror("calloc()"); \ -+ return NULL; \ -+ } \ -+ snprintf(buffer,REQUEST_USER_SIZE(pwname),"(&(objectclass=posixAccount)(objectclass=ldapPublicKey)(uid=%s))",pwname) -+ -+/* some portable and working tokenizer, lame though */ -+static int tokenize(char ** o, size_t size, char * input) { -+ unsigned int i = 0, num; -+ char * charset = " \t"; -+ char * ptr = input; -+ -+ /* leading white spaces are ignored */ -+ num = strspn(ptr, charset); -+ ptr += num; -+ -+ while ((num = strcspn(ptr, charset))) { -+ if (i < size-1) { -+ o[i++] = ptr; -+ ptr += num; -+ if (*ptr) -+ *ptr++ = '\0'; -+ } -+ } -+ o[i] = NULL; -+ return SUCCESS; -+} -+ -+void ldap_close(ldap_opt_t * ldap) { -+ -+ if (!ldap) -+ return; -+ -+ if ( ldap_unbind_ext(ldap->ld, NULL, NULL) < 0) -+ ldap_perror(ldap->ld, "ldap_unbind()"); -+ -+ ldap->ld = NULL; -+ FLAG_SET_DISCONNECTED(ldap->flags); -+ -+ return; -+} -+ -+/* init && bind */ -+int ldap_connect(ldap_opt_t * ldap) { -+ int version = LDAP_VERSION3; -+ -+ if (!ldap->servers) -+ return FAILURE; -+ -+ /* Connection Init and setup */ -+ ldap->ld = ldap_init(ldap->servers, LDAP_PORT); -+ if (!ldap->ld) { -+ ldap_perror(ldap->ld, "ldap_init()"); -+ return FAILURE; -+ } -+ -+ if ( ldap_set_option(ldap->ld, LDAP_OPT_PROTOCOL_VERSION, &version) != LDAP_OPT_SUCCESS) { -+ ldap_perror(ldap->ld, "ldap_set_option(LDAP_OPT_PROTOCOL_VERSION)"); -+ return FAILURE; -+ } -+ -+ /* Timeouts setup */ -+ if (ldap_set_option(ldap->ld, LDAP_OPT_NETWORK_TIMEOUT, &ldap->b_timeout) != LDAP_SUCCESS) { -+ ldap_perror(ldap->ld, "ldap_set_option(LDAP_OPT_NETWORK_TIMEOUT)"); -+ } -+ if (ldap_set_option(ldap->ld, LDAP_OPT_TIMEOUT, &ldap->s_timeout) != LDAP_SUCCESS) { -+ ldap_perror(ldap->ld, "ldap_set_option(LDAP_OPT_TIMEOUT)"); -+ } -+ -+ /* TLS support */ -+ if ( (ldap->tls == -1) || (ldap->tls == 1) ) { -+ if (ldap_start_tls_s(ldap->ld, NULL, NULL ) != LDAP_SUCCESS) { -+ /* failed then reinit the initial connect */ -+ ldap_perror(ldap->ld, "ldap_connect: (TLS) ldap_start_tls()"); -+ if (ldap->tls == 1) -+ return FAILURE; -+ -+ ldap->ld = ldap_init(ldap->servers, LDAP_PORT); -+ if (!ldap->ld) { -+ ldap_perror(ldap->ld, "ldap_init()"); -+ return FAILURE; -+ } -+ -+ if ( ldap_set_option(ldap->ld, LDAP_OPT_PROTOCOL_VERSION, &version) != LDAP_OPT_SUCCESS) { -+ ldap_perror(ldap->ld, "ldap_set_option()"); -+ return FAILURE; -+ } -+ } -+ } -+ -+ -+ if ( ldap_simple_bind_s(ldap->ld, ldap->binddn, ldap->bindpw) != LDAP_SUCCESS) { -+ ldap_perror(ldap->ld, "ldap_simple_bind_s()"); -+ return FAILURE; -+ } -+ -+ /* says it is connected */ -+ FLAG_SET_CONNECTED(ldap->flags); -+ -+ return SUCCESS; -+} -+ -+/* must free allocated ressource */ -+static char * ldap_build_host(char *host, int port) { -+ unsigned int size = strlen(host)+11; -+ char * h = (char *) calloc (size, sizeof(char)); -+ int rc; -+ if (!h) -+ return NULL; -+ -+ rc = snprintf(h, size, "%s:%d ", host, port); -+ if (rc == -1) -+ return NULL; -+ return h; -+} -+ -+static int ldap_count_group(char * input) { -+ char * charset = " \t"; -+ char * ptr = input; -+ unsigned int count = 0; -+ unsigned int num; -+ -+ num = strspn(ptr, charset); -+ ptr += num; -+ -+ while ((num = strcspn(ptr, charset))) { -+ count++; -+ ptr += num; -+ ptr++; -+ } -+ -+ return count; -+} -+ -+/* format filter */ -+char * ldap_parse_groups(char * groups) { -+ unsigned int buffer_size = FILTER_GROUP_SIZE(groups); -+ char * buffer = (char *) calloc(buffer_size, sizeof(char)); -+ char * g = NULL; -+ char * garray[32]; -+ unsigned int i = 0; -+ -+ if ((!groups)||(!buffer)) -+ return NULL; -+ -+ g = strdup(groups); -+ if (!g) { -+ free(buffer); -+ return NULL; -+ } -+ -+ /* first separate into n tokens */ -+ if ( tokenize(garray, sizeof(garray)/sizeof(*garray), g) < 0) { -+ free(g); -+ free(buffer); -+ return NULL; -+ } -+ -+ /* build the final filter format */ -+ strlcat(buffer, FILTER_GROUP_PREFIX, buffer_size); -+ strlcat(buffer, FILTER_OR_PREFIX, buffer_size); -+ i = 0; -+ while (garray[i]) { -+ strlcat(buffer, FILTER_CN_PREFIX, buffer_size); -+ strlcat(buffer, garray[i], buffer_size); -+ strlcat(buffer, FILTER_CN_SUFFIX, buffer_size); -+ i++; -+ } -+ strlcat(buffer, FILTER_OR_SUFFIX, buffer_size); -+ strlcat(buffer, FILTER_UID_FORMAT, buffer_size); -+ strlcat(buffer, FILTER_GROUP_SUFFIX, buffer_size); -+ -+ free(g); -+ return buffer; -+} -+ -+/* a bit dirty but leak free */ -+char * ldap_parse_servers(char * servers) { -+ char * s = NULL; -+ char * tmp = NULL, *urls[32]; -+ unsigned int num = 0 , i = 0 , asize = 0; -+ LDAPURLDesc *urld[32]; -+ -+ if (!servers) -+ return NULL; -+ -+ /* local copy of the arg */ -+ s = strdup(servers); -+ if (!s) -+ return NULL; -+ -+ /* first separate into URL tokens */ -+ if ( tokenize(urls, sizeof(urls)/sizeof(*urls), s) < 0) -+ return NULL; -+ -+ i = 0; -+ while (urls[i]) { -+ if ( ldap_is_ldap_url(urls[i]) ) { -+ if (ldap_url_parse(urls[i], &urld[i]) != 0) -+ return NULL; -+ } -+ i++; -+ } -+ -+ /* now free(s) */ -+ free (s); -+ -+ /* how much memory do we need */ -+ num = i; -+ for (i = 0 ; i < num ; i++) -+ asize += strlen(urld[i]->lud_host)+11; -+ -+ /* alloc */ -+ s = (char *) calloc( asize+1 , sizeof(char)); -+ if (!s) { -+ for (i = 0 ; i < num ; i++) -+ ldap_free_urldesc(urld[i]); -+ return NULL; -+ } -+ -+ /* then build the final host string */ -+ for (i = 0 ; i < num ; i++) { -+ /* built host part */ -+ tmp = ldap_build_host(urld[i]->lud_host, urld[i]->lud_port); -+ strncat(s, tmp, strlen(tmp)); -+ ldap_free_urldesc(urld[i]); -+ free(tmp); -+ } -+ -+ return s; -+} -+ -+void ldap_options_print(ldap_opt_t * ldap) { -+ printf("ldap options:\n"); -+ printf("servers: %s\n", ldap->servers); -+ if (ldap->u_basedn) -+ printf("user basedn: %s\n", ldap->u_basedn); -+ if (ldap->g_basedn) -+ printf("group basedn: %s\n", ldap->g_basedn); -+ if (ldap->binddn) -+ printf("binddn: %s\n", ldap->binddn); -+ if (ldap->bindpw) -+ printf("bindpw: %s\n", ldap->bindpw); -+ if (ldap->sgroup) -+ printf("group: %s\n", ldap->sgroup); -+} -+ -+void ldap_options_free(ldap_opt_t * l) { -+ if (!l) -+ return; -+ if (l->servers) -+ free(l->servers); -+ if (l->u_basedn) -+ free(l->u_basedn); -+ if (l->g_basedn) -+ free(l->g_basedn); -+ if (l->binddn) -+ free(l->binddn); -+ if (l->bindpw) -+ free(l->bindpw); -+ if (l->sgroup) -+ free(l->sgroup); -+ if (l->fgroup) -+ free(l->fgroup); -+ if (l->l_conf) -+ free(l->l_conf); -+ free(l); -+} -+ -+/* free keys */ -+void ldap_keys_free(ldap_key_t * k) { -+ ldap_value_free_len(k->keys); -+ free(k); -+ return; -+} -+ -+ldap_key_t * ldap_getuserkey(ldap_opt_t *l, char * user) { -+ ldap_key_t * k = (ldap_key_t *) calloc (1, sizeof(ldap_key_t)); -+ LDAPMessage *res, *e; -+ char * filter; -+ int i; -+ -+ if ((!k) || (!l)) -+ return NULL; -+ -+ /* Am i still connected ? RETRY n times */ -+ /* XXX TODO: setup some conf value for retrying */ -+ if (!(l->flags & FLAG_CONNECTED)) -+ for (i = 0 ; i < 2 ; i++) -+ if (ldap_connect(l) == 0) -+ break; -+ -+ /* build filter for LDAP request */ -+ REQUEST_USER(filter, user); -+ -+ if ( ldap_search_st( l->ld, -+ l->u_basedn, -+ LDAP_SCOPE_SUBTREE, -+ filter, -+ attrs, 0, &l->s_timeout, &res ) != LDAP_SUCCESS) { -+ -+ ldap_perror(l->ld, "ldap_search_st()"); -+ -+ free(filter); -+ free(k); -+ -+ /* XXX error on search, timeout etc.. close ask for reconnect */ -+ ldap_close(l); -+ -+ return NULL; -+ } -+ -+ /* free */ -+ free(filter); -+ -+ /* check if any results */ -+ i = ldap_count_entries(l->ld,res); -+ if (i <= 0) { -+ ldap_msgfree(res); -+ free(k); -+ return NULL; -+ } -+ -+ if (i > 1) -+ printf("[LDAP] duplicate entries, using the FIRST entry returned\n"); -+ -+ e = ldap_first_entry(l->ld, res); -+ k->keys = ldap_get_values_len(l->ld, e, PUBKEYATTR); -+ k->num = ldap_count_values_len(k->keys); -+ -+ ldap_msgfree(res); -+ return k; -+} -+ -+ -+/* -1 if trouble -+ 0 if user is NOT member of current server group -+ 1 if user IS MEMBER of current server group -+ */ -+int ldap_ismember(ldap_opt_t * l, char * user) { -+ LDAPMessage *res; -+ char * filter; -+ int i; -+ -+ if ((!l->sgroup) || !(l->g_basedn)) -+ return 1; -+ -+ /* Am i still connected ? RETRY n times */ -+ /* XXX TODO: setup some conf value for retrying */ -+ if (!(l->flags & FLAG_CONNECTED)) -+ for (i = 0 ; i < 2 ; i++) -+ if (ldap_connect(l) == 0) -+ break; -+ -+ /* build filter for LDAP request */ -+ REQUEST_GROUP(filter, l->fgroup, user); -+ -+ if (ldap_search_st( l->ld, -+ l->g_basedn, -+ LDAP_SCOPE_SUBTREE, -+ filter, -+ NULL, 0, &l->s_timeout, &res) != LDAP_SUCCESS) { -+ -+ ldap_perror(l->ld, "ldap_search_st()"); -+ -+ free(filter); -+ -+ /* XXX error on search, timeout etc.. close ask for reconnect */ -+ ldap_close(l); -+ -+ return FAILURE; -+ } -+ -+ free(filter); -+ -+ /* check if any results */ -+ if (ldap_count_entries(l->ld, res) > 0) { -+ ldap_msgfree(res); -+ return 1; -+ } -+ -+ ldap_msgfree(res); -+ return 0; -+} -+ -+/* -+ * ldap.conf simple parser -+ * XXX TODO: sanity checks -+ * must either -+ * - free the previous ldap_opt_before replacing entries -+ * - free each necessary previously parsed elements -+ * ret: -+ * -1 on FAILURE, 0 on SUCCESS -+ */ -+int ldap_parse_lconf(ldap_opt_t * l) { -+ FILE * lcd; /* ldap.conf descriptor */ -+ char buf[BUFSIZ]; -+ char * s = NULL, * k = NULL, * v = NULL; -+ int li, len; -+ -+ lcd = fopen (l->l_conf, "r"); -+ if (lcd == NULL) { -+ /* debug("Cannot open %s", l->l_conf); */ -+ perror("ldap_parse_lconf()"); -+ return FAILURE; -+ } -+ -+ while (fgets (buf, sizeof (buf), lcd) != NULL) { -+ -+ if (*buf == '\n' || *buf == '#') -+ continue; -+ -+ k = buf; -+ v = k; -+ while (*v != '\0' && *v != ' ' && *v != '\t') -+ v++; -+ -+ if (*v == '\0') -+ continue; -+ -+ *(v++) = '\0'; -+ -+ while (*v == ' ' || *v == '\t') -+ v++; -+ -+ li = strlen (v) - 1; -+ while (v[li] == ' ' || v[li] == '\t' || v[li] == '\n') -+ --li; -+ v[li + 1] = '\0'; -+ -+ if (!strcasecmp (k, "uri")) { -+ if ((l->servers = ldap_parse_servers(strdup (v))) == NULL) { -+ fatal("error in ldap servers"); -+ return FAILURE; -+ } -+ -+ } -+ else if (!strcasecmp (k, "base")) { -+ s = strchr (v, '?'); -+ if (s != NULL) { -+ len = s - v; -+ l->u_basedn = malloc (len + 1); -+ strncpy (l->u_basedn, v, len); -+ l->u_basedn[len] = '\0'; -+ } else { -+ l->u_basedn = strdup (v); -+ } -+ } -+ else if (!strcasecmp (k, "binddn")) { -+ l->binddn = strdup (v); -+ } -+ else if (!strcasecmp (k, "bindpw")) { -+ l->bindpw = strdup (v); -+ } -+ else if (!strcasecmp (k, "timelimit")) { -+ l->s_timeout.tv_sec = atoi (v); -+ } -+ else if (!strcasecmp (k, "bind_timelimit")) { -+ l->b_timeout.tv_sec = atoi (v); -+ } -+ else if (!strcasecmp (k, "ssl")) { -+ if (!strcasecmp (v, "start_tls")) -+ l->tls = 1; -+ } -+ } -+ -+ fclose (lcd); -+ return SUCCESS; -+} -+ -+#endif /* WITH_LDAP_PUBKEY */ ---- ldapauth.h.orig Sat Oct 7 17:26:32 2006 -+++ ldapauth.h Sat Oct 7 17:26:32 2006 -@@ -0,0 +1,119 @@ -+/* -+ * $Id: openssh-lpk-4.3p1-0.3.7.patch,v 1.3 2006/04/18 15:29:09 eau Exp $ -+ */ -+ -+/* -+ * -+ * Copyright (c) 2005, Eric AUGE <eau@phear.org> -+ * All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: -+ * -+ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. -+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -+ * Neither the name of the phear.org nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, -+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -+ * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, -+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ * -+ * -+ */ -+ -+#ifndef LDAPAUTH_H -+#define LDAPAUTH_H -+ -+#include <string.h> -+#include <time.h> -+#include <ldap.h> -+#include <lber.h> -+ -+/* tokens in use for config */ -+#define _DEFAULT_LPK_TOKEN "UseLPK" -+#define _DEFAULT_SRV_TOKEN "LpkServers" -+#define _DEFAULT_USR_TOKEN "LpkUserDN" -+#define _DEFAULT_GRP_TOKEN "LpkGroupDN" -+#define _DEFAULT_BDN_TOKEN "LpkBindDN" -+#define _DEFAULT_BPW_TOKEN "LpkBindPw" -+#define _DEFAULT_MYG_TOKEN "LpkServerGroup" -+#define _DEFAULT_TLS_TOKEN "LpkForceTLS" -+#define _DEFAULT_BTI_TOKEN "LpkBindTimelimit" -+#define _DEFAULT_STI_TOKEN "LpkSearchTimelimit" -+#define _DEFAULT_LDP_TOKEN "LpkLdapConf" -+ -+/* default options */ -+#define _DEFAULT_LPK_ON 0 -+#define _DEFAULT_LPK_SERVERS NULL -+#define _DEFAULT_LPK_UDN NULL -+#define _DEFAULT_LPK_GDN NULL -+#define _DEFAULT_LPK_BINDDN NULL -+#define _DEFAULT_LPK_BINDPW NULL -+#define _DEFAULT_LPK_SGROUP NULL -+#define _DEFAULT_LPK_TLS -1 -+#define _DEFAULT_LPK_BTIMEOUT 10 -+#define _DEFAULT_LPK_STIMEOUT 10 -+#define _DEFAULT_LPK_LDP NULL -+ -+/* flags */ -+#define FLAG_EMPTY 0x00000000 -+#define FLAG_CONNECTED 0x00000001 -+ -+/* flag macros */ -+#define FLAG_SET_EMPTY(x) x&=(FLAG_EMPTY) -+#define FLAG_SET_CONNECTED(x) x|=(FLAG_CONNECTED) -+#define FLAG_SET_DISCONNECTED(x) x&=~(FLAG_CONNECTED) -+ -+/* defines */ -+#define FAILURE -1 -+#define SUCCESS 0 -+#define PUBKEYATTR "sshPublicKey" -+ -+/* -+ * -+ * defined files path -+ * (should be relocated to pathnames.h, -+ * if one day it's included within the tree) -+ * -+ */ -+#define _PATH_LDAP_CONFIG_FILE "/etc/ldap.conf" -+ -+/* structures */ -+typedef struct ldap_options { -+ int on; /* Use it or NOT */ -+ LDAP * ld; /* LDAP file desc */ -+ char * servers; /* parsed servers for ldaplib failover handling */ -+ char * u_basedn; /* user basedn */ -+ char * g_basedn; /* group basedn */ -+ char * binddn; /* binddn */ -+ char * bindpw; /* bind password */ -+ char * sgroup; /* server group */ -+ char * fgroup; /* group filter */ -+ char * l_conf; /* use ldap.conf */ -+ int tls; /* TLS only */ -+ struct timeval b_timeout; /* bind timeout */ -+ struct timeval s_timeout; /* search timeout */ -+ unsigned int flags; /* misc flags (reconnection, future use?) */ -+} ldap_opt_t; -+ -+typedef struct ldap_keys { -+ struct berval ** keys; /* the public keys retrieved */ -+ unsigned int num; /* number of keys */ -+} ldap_key_t; -+ -+ -+/* function headers */ -+void ldap_close(ldap_opt_t *); -+int ldap_connect(ldap_opt_t *); -+char * ldap_parse_groups(char *); -+char * ldap_parse_servers(char *); -+void ldap_options_print(ldap_opt_t *); -+void ldap_options_free(ldap_opt_t *); -+void ldap_keys_free(ldap_key_t *); -+int ldap_parse_lconf(ldap_opt_t *); -+ldap_key_t * ldap_getuserkey(ldap_opt_t *, char *); -+int ldap_ismember(ldap_opt_t *, char *); -+ -+#endif ---- lpk-user-example.txt.orig Sat Oct 7 17:26:32 2006 -+++ lpk-user-example.txt Sat Oct 7 17:26:32 2006 -@@ -0,0 +1,117 @@ -+ -+Post to ML -> User Made Quick Install Doc. -+Contribution from John Lane <john@lane.uk.net> -+ -+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -+ -+OpenSSH LDAP keystore Patch -+=========================== -+ -+NOTE: these notes are a transcript of a specific installation -+ they work for me, your specifics may be different! -+ from John Lane March 17th 2005 john@lane.uk.net -+ -+This is a patch to OpenSSH 4.0p1 to allow it to obtain users' public keys -+from their LDAP record as an alternative to ~/.ssh/authorized_keys. -+ -+(Assuming here that necessary build stuff is in $BUILD) -+ -+cd $BUILD/openssh-4.0p1 -+patch -Np1 -i $BUILD/openssh-lpk-4.0p1-0.3.patch -+mkdir -p /var/empty && -+./configure --prefix=/usr --sysconfdir=/etc/ssh \ -+ --libexecdir=/usr/sbin --with-md5-passwords --with-pam \ -+ --with-libs="-lldap" --with-cppflags="-DWITH_LDAP_PUBKEY" -+Now do. -+make && -+make install -+ -+Add the following config to /etc/ssh/ssh_config -+UseLPK yes -+LpkServers ldap://myhost.mydomain.com -+LpkUserDN ou=People,dc=mydomain,dc=com -+ -+We need to tell sshd about the SSL keys during boot, as root's -+environment does not exist at that time. Edit /etc/rc.d/init.d/sshd. -+Change the startup code from this: -+ echo "Starting SSH Server..." -+ loadproc /usr/sbin/sshd -+ ;; -+to this: -+ echo "Starting SSH Server..." -+ LDAPRC="/root/.ldaprc" loadproc /usr/sbin/sshd -+ ;; -+ -+Re-start the sshd daemon: -+/etc/rc.d/init.d/sshd restart -+ -+Install the additional LDAP schema -+cp $BUILD/openssh-lpk-0.2.schema /etc/openldap/schema/openssh.schema -+ -+Now add the openSSH LDAP schema to /etc/openldap/slapd.conf: -+Add the following to the end of the existing block of schema includes -+include /etc/openldap/schema/openssh.schema -+ -+Re-start the LDAP server: -+/etc/rc.d/init.d/slapd restart -+ -+To add one or more public keys to a user, eg "testuser" : -+ldapsearch -x -W -Z -LLL -b "uid=testuser,ou=People,dc=mydomain,dc=com" -D -+"uid=testuser,ou=People,dc=mydomain,dc=com" > /tmp/testuser -+ -+append the following to this /tmp/testuser file -+objectclass: ldapPublicKey -+sshPublicKey: ssh-rsa -+AAAAB3NzaC1yc2EAAAABJQAAAIB3dsrwqXqD7E4zYYrxwdDKBUQxKMioXy9pxFVai64kAPxjU9KS -+qIo7QfkjslfsjflksjfldfkjsldfjLX/5zkzRmT28I5piGzunPv17S89z8XwSsuAoR1t86t+5dlI -+7eZE/gVbn2UQkQq7+kdDTS2yXV6VnC52N/kKLG3ciBkBAw== General Purpose RSA Key -+ -+Then do a modify: -+ldapmodify -x -D "uid=testuser,ou=People,dc=mydomain,dc=com" -W -f -+/tmp/testuser -Z -+Enter LDAP Password: -+modifying entry "uid=testuser,ou=People,dc=mydomain,dc=com" -+And check the modify is ok: -+ldapsearch -x -W -Z -b "uid=testuser,ou=People,dc=mydomain,dc=com" -D -+"uid=testuser,ou=People,dc=mydomain,dc=com" -+Enter LDAP Password: -+# extended LDIF -+# -+# LDAPv3 -+# base <uid=testuser,ou=People,dc=mydomain,dc=com> with scope sub -+# filter: (objectclass=*) -+# requesting: ALL -+# -+ -+# testuser, People, mydomain.com -+dn: uid=testuser,ou=People,dc=mydomain,dc=com -+uid: testuser -+cn: testuser -+objectClass: account -+objectClass: posixAccount -+objectClass: top -+objectClass: shadowAccount -+objectClass: ldapPublicKey -+shadowLastChange: 12757 -+shadowMax: 99999 -+shadowWarning: 7 -+loginShell: /bin/bash -+uidNumber: 9999 -+gidNumber: 501 -+homeDirectory: /home/testuser -+userPassword:: e1NTSEF9UDgwV1hnM1VjUDRJK0k1YnFiL1d4ZUJObXlZZ3Z3UTU= -+sshPublicKey: ssh-rsa -+AAAAB3NzaC1yc2EAAAABJQAAAIB3dsrwqXqD7E4zYYrxwdDKBUQxKMioXy9pxFVai64kAPxjU9KSqIo7QfkjslfsjflksjfldfkjsldfjLX/5zkzRmT28I5piGzunPv17S89z -+8XwSsuAoR1t86t+5dlI7eZE/gVbn2UQkQq7+kdDTS2yXV6VnC52N/kKLG3ciBkBAw== General Purpose RSA Key -+ -+# search result -+search: 3 -+result: 0 Success -+ -+# numResponses: 2 -+# numEntries: 1 -+ -+Now start a ssh session to user "testuser" from usual ssh client (e.g. -+puTTY). Login should succeed. -+ -+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ---- openssh-lpk.schema.orig Sat Oct 7 17:26:32 2006 -+++ openssh-lpk.schema Sat Oct 7 17:26:32 2006 -@@ -0,0 +1,21 @@ -+# -+# $Id: openssh-lpk-4.3p1-0.3.7.patch,v 1.3 2006/04/18 15:29:09 eau Exp $ -+# -+# LDAP Public Key Patch schema for use with openssh-ldappubkey -+# Author: Eric AUGE <eau@phear.org> -+# -+# Based on the proposal of : Mark Ruijter -+# -+ -+ -+# octetString SYNTAX -+attributetype ( 1.3.6.1.4.1.22054.500.1.1.1.13 NAME 'sshPublicKey' -+ DESC 'MANDATORY: OpenSSH Public key' -+ EQUALITY octetStringMatch -+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 ) -+ -+# printableString SYNTAX yes|no -+objectclass ( 1.3.6.1.4.1.22054.500.1.1.2.0 NAME 'ldapPublicKey' SUP top AUXILIARY -+ DESC 'MANDATORY: OpenSSH LPK objectclass' -+ MUST ( sshPublicKey $ uid ) -+ ) ---- servconf.c.orig Fri Aug 18 11:23:15 2006 -+++ servconf.c Sat Oct 7 17:31:53 2006 -@@ -40,6 +40,10 @@ - #include "channels.h" - #include "groupaccess.h" - -+#ifdef WITH_LDAP_PUBKEY -+#include "ldapauth.h" -+#endif -+ - static void add_listen_addr(ServerOptions *, char *, u_short); - static void add_one_listen_addr(ServerOptions *, char *, u_short); - -@@ -120,6 +124,23 @@ - options->authorized_keys_file2 = NULL; - options->num_accept_env = 0; - options->permit_tun = -1; -+#ifdef WITH_LDAP_PUBKEY -+ /* XXX dirty */ -+ options->lpk.ld = NULL; -+ options->lpk.on = -1; -+ options->lpk.servers = NULL; -+ options->lpk.u_basedn = NULL; -+ options->lpk.g_basedn = NULL; -+ options->lpk.binddn = NULL; -+ options->lpk.bindpw = NULL; -+ options->lpk.sgroup = NULL; -+ options->lpk.fgroup = NULL; -+ options->lpk.l_conf = NULL; -+ options->lpk.tls = -1; -+ options->lpk.b_timeout.tv_sec = 0; -+ options->lpk.s_timeout.tv_sec = 0; -+ options->lpk.flags = FLAG_EMPTY; -+#endif - options->num_permitted_opens = -1; - options->adm_forced_command = NULL; - } -@@ -249,6 +270,30 @@ - options->authorized_keys_file = _PATH_SSH_USER_PERMITTED_KEYS; - if (options->permit_tun == -1) - options->permit_tun = SSH_TUNMODE_NO; -+#ifdef WITH_LDAP_PUBKEY -+ if (options->lpk.on == -1) -+ options->lpk.on = _DEFAULT_LPK_ON; -+ if (options->lpk.servers == NULL) -+ options->lpk.servers = _DEFAULT_LPK_SERVERS; -+ if (options->lpk.u_basedn == NULL) -+ options->lpk.u_basedn = _DEFAULT_LPK_UDN; -+ if (options->lpk.g_basedn == NULL) -+ options->lpk.g_basedn = _DEFAULT_LPK_GDN; -+ if (options->lpk.binddn == NULL) -+ options->lpk.binddn = _DEFAULT_LPK_BINDDN; -+ if (options->lpk.bindpw == NULL) -+ options->lpk.bindpw = _DEFAULT_LPK_BINDPW; -+ if (options->lpk.sgroup == NULL) -+ options->lpk.sgroup = _DEFAULT_LPK_SGROUP; -+ if (options->lpk.tls == -1) -+ options->lpk.tls = _DEFAULT_LPK_TLS; -+ if (options->lpk.b_timeout.tv_sec == 0) -+ options->lpk.b_timeout.tv_sec = _DEFAULT_LPK_BTIMEOUT; -+ if (options->lpk.s_timeout.tv_sec == 0) -+ options->lpk.s_timeout.tv_sec = _DEFAULT_LPK_STIMEOUT; -+ if (options->lpk.l_conf == NULL) -+ options->lpk.l_conf = _DEFAULT_LPK_LDP; -+#endif - - /* Turn privilege separation on by default */ - if (use_privsep == -1) -@@ -294,6 +339,12 @@ - sMatch, sPermitOpen, sForceCommand, - sUsePrivilegeSeparation, - sDeprecated, sUnsupported -+#ifdef WITH_LDAP_PUBKEY -+ ,sLdapPublickey, sLdapServers, sLdapUserDN -+ ,sLdapGroupDN, sBindDN, sBindPw, sMyGroup -+ ,sForceTLS, sBindTimeout, sSearchTimeout -+ ,sLdapConf -+#endif - } ServerOpCodes; - - #define SSHCFG_GLOBAL 0x01 /* allowed in main section of sshd_config */ -@@ -397,6 +448,19 @@ - { "clientalivecountmax", sClientAliveCountMax, SSHCFG_GLOBAL }, - { "authorizedkeysfile", sAuthorizedKeysFile, SSHCFG_GLOBAL }, - { "authorizedkeysfile2", sAuthorizedKeysFile2, SSHCFG_GLOBAL }, -+#ifdef WITH_LDAP_PUBKEY -+ { _DEFAULT_LPK_TOKEN, sLdapPublickey }, -+ { _DEFAULT_SRV_TOKEN, sLdapServers }, -+ { _DEFAULT_USR_TOKEN, sLdapUserDN }, -+ { _DEFAULT_GRP_TOKEN, sLdapGroupDN }, -+ { _DEFAULT_BDN_TOKEN, sBindDN }, -+ { _DEFAULT_BPW_TOKEN, sBindPw }, -+ { _DEFAULT_MYG_TOKEN, sMyGroup }, -+ { _DEFAULT_TLS_TOKEN, sForceTLS }, -+ { _DEFAULT_BTI_TOKEN, sBindTimeout }, -+ { _DEFAULT_STI_TOKEN, sSearchTimeout }, -+ { _DEFAULT_LDP_TOKEN, sLdapConf }, -+#endif - { "useprivilegeseparation", sUsePrivilegeSeparation, SSHCFG_GLOBAL }, - { "acceptenv", sAcceptEnv, SSHCFG_GLOBAL }, - { "permittunnel", sPermitTunnel, SSHCFG_GLOBAL }, -@@ -1266,6 +1330,116 @@ - while (arg) - arg = strdelim(&cp); - break; -+#ifdef WITH_LDAP_PUBKEY -+ case sLdapPublickey: -+ intptr = &options->lpk.on; -+ goto parse_flag; -+ case sLdapServers: -+ /* arg = strdelim(&cp); */ -+ p = line; -+ while(*p++); -+ arg = p; -+ if (!arg || *arg == '\0') -+ fatal("%s line %d: missing ldap server",filename,linenum); -+ arg[strlen(arg)] = '\0'; -+ if ((options->lpk.servers = ldap_parse_servers(arg)) == NULL) -+ fatal("%s line %d: error in ldap servers", filename, linenum); -+ memset(arg,0,strlen(arg)); -+ break; -+ case sLdapUserDN: -+ /* arg = strdelim(&cp); */ -+ p = line; -+ while(*p++); -+ arg = p; -+ if (!arg || *arg == '\0') -+ fatal("%s line %d: missing ldap server",filename,linenum); -+ arg[strlen(arg)] = '\0'; -+ options->lpk.u_basedn = xstrdup(arg); -+ memset(arg,0,strlen(arg)); -+ break; -+ case sLdapGroupDN: -+ /* arg = strdelim(&cp); */ -+ p = line; -+ while(*p++); -+ arg = p; -+ if (!arg || *arg == '\0') -+ fatal("%s line %d: missing ldap server",filename,linenum); -+ arg[strlen(arg)] = '\0'; -+ options->lpk.g_basedn = xstrdup(arg); -+ memset(arg,0,strlen(arg)); -+ break; -+ case sBindDN: -+ /* arg = strdelim(&cp); */ -+ p = line; -+ while(*p++); -+ arg = p; -+ if (!arg || *arg == '\0') -+ fatal("%s line %d: missing binddn",filename,linenum); -+ arg[strlen(arg)] = '\0'; -+ options->lpk.binddn = xstrdup(arg); -+ memset(arg,0,strlen(arg)); -+ break; -+ case sBindPw: -+ /* arg = strdelim(&cp); */ -+ p = line; -+ while(*p++); -+ arg = p; -+ if (!arg || *arg == '\0') -+ fatal("%s line %d: missing bindpw",filename,linenum); -+ arg[strlen(arg)] = '\0'; -+ options->lpk.bindpw = xstrdup(arg); -+ memset(arg,0,strlen(arg)); -+ break; -+ case sMyGroup: -+ p = line; -+ while (*p++); -+ arg = p; -+ if (!arg || *arg == '\0') -+ fatal("%s line %d: missing groupname",filename, linenum); -+ arg[strlen(arg)] = '\0'; -+ options->lpk.sgroup = xstrdup(arg); -+ if (options->lpk.sgroup) -+ options->lpk.fgroup = ldap_parse_groups(options->lpk.sgroup); -+ memset(arg,0,strlen(arg)); -+ break; -+ case sForceTLS: -+ intptr = &options->lpk.tls; -+ arg = strdelim(&cp); -+ if (!arg || *arg == '\0') -+ fatal("%s line %d: missing yes/no argument.", -+ filename, linenum); -+ value = 0; /* silence compiler */ -+ if (strcmp(arg, "yes") == 0) -+ value = 1; -+ else if (strcmp(arg, "no") == 0) -+ value = 0; -+ else if (strcmp(arg, "try") == 0) -+ value = -1; -+ else -+ fatal("%s line %d: Bad yes/no argument: %s", -+ filename, linenum, arg); -+ if (*intptr == -1) -+ *intptr = value; -+ break; -+ case sBindTimeout: -+ intptr = (int *) &options->lpk.b_timeout.tv_sec; -+ goto parse_int; -+ case sSearchTimeout: -+ intptr = (int *) &options->lpk.s_timeout.tv_sec; -+ goto parse_int; -+ break; -+ case sLdapConf: -+ /* arg = strdelim(&cp); */ -+ p = line; -+ while (*p++); -+ arg = p; -+ if (!arg || *arg == '\0') -+ fatal("%s line %d: missing LpkLdapConf", filename, linenum); -+ arg[strlen(arg)] = '\0'; -+ options->lpk.l_conf = xstrdup(arg); -+ memset(arg, 0, strlen(arg)); -+ break; -+#endif - - default: - fatal("%s line %d: Missing handler for opcode %s (%d)", ---- servconf.h.orig Fri Aug 18 11:23:15 2006 -+++ servconf.h Sat Oct 7 17:32:38 2006 -@@ -16,6 +16,10 @@ - #ifndef SERVCONF_H - #define SERVCONF_H - -+#ifdef WITH_LDAP_PUBKEY -+#include "ldapauth.h" -+#endif -+ - #define MAX_PORTS 256 /* Max # ports. */ - - #define MAX_ALLOW_USERS 256 /* Max # users on allow list. */ -@@ -139,6 +143,10 @@ - int use_pam; /* Enable auth via PAM */ - - int permit_tun; -+ -+#ifdef WITH_LDAP_PUBKEY -+ ldap_opt_t lpk; -+#endif - - int num_permitted_opens; - } ServerOptions; ---- sshd.c.orig Sun Sep 17 01:04:46 2006 -+++ sshd.c Sat Oct 7 17:26:32 2006 -@@ -124,6 +124,10 @@ - int deny_severity = LOG_WARNING; - #endif /* LIBWRAP */ - -+#ifdef WITH_LDAP_PUBKEY -+#include "ldapauth.h" -+#endif -+ - #ifndef O_NOCTTY - #define O_NOCTTY 0 - #endif -@@ -1429,6 +1433,16 @@ - exit(1); - } - -+#ifdef WITH_LDAP_PUBKEY -+ /* ldap_options_print(&options.lpk); */ -+ /* XXX initialize/check ldap connection and set *LD */ -+ if (options.lpk.on) { -+ if (options.lpk.l_conf && (ldap_parse_lconf(&options.lpk) < 0) ) -+ error("[LDAP] could not parse %s", options.lpk.l_conf); -+ if (ldap_connect(&options.lpk) < 0) -+ error("[LDAP] could not initialize ldap connection"); -+ } -+#endif - debug("sshd version %.100s", SSH_RELEASE); - - /* Store privilege separation user for later use */ ---- sshd_config.orig Mon Jul 24 01:06:47 2006 -+++ sshd_config Sat Oct 7 17:26:32 2006 -@@ -101,6 +101,20 @@ - - # no default banner path - #Banner /some/path -+ -+# here are the new patched ldap related tokens -+# entries in your LDAP must have posixAccount & ldapPublicKey objectclass -+#UseLPK yes -+#LpkLdapConf /etc/ldap.conf -+#LpkServers ldap://10.1.7.1 ldap://10.1.7.2 -+#LpkUserDN ou=users,dc=phear,dc=org -+#LpkGroupDN ou=groups,dc=phear,dc=org -+#LpkBindDN cn=Manager,dc=phear,dc=org -+#LpkBindPw secret -+#LpkServerGroup mail -+#LpkForceTLS no -+#LpkSearchTimelimit 3 -+#LpkBindTimelimit 3 - - # override default of no subsystems - Subsystem sftp /usr/libexec/sftp-server ---- sshd_config.5.orig Tue Aug 29 22:06:34 2006 -+++ sshd_config.5 Sat Oct 7 17:26:32 2006 -@@ -897,6 +897,58 @@ - program. - The default is - .Pa /usr/X11R6/bin/xauth . -+.It Cm UseLPK -+Specifies whether LDAP public key retrieval must be used or not. It allow -+an easy centralisation of public keys within an LDAP directory. The argument must be -+.Dq yes -+or -+.Dq no . -+.It Cm LpkLdapConf -+Specifies whether LDAP Public keys should parse the specified ldap.conf file -+instead of sshd_config Tokens. The argument must be a valid path to an ldap.conf -+file like -+.Pa /etc/ldap.conf -+.It Cm LpkServers -+Specifies LDAP one or more [:space:] separated server's url the following form may be used: -+.Pp -+LpkServers ldaps://127.0.0.1 ldap://127.0.0.2 ldap://127.0.0.3 -+.It Cm LpkUserDN -+Specifies the LDAP user DN. -+.Pp -+LpkUserDN ou=users,dc=phear,dc=org -+.It Cm LpkGroupDN -+Specifies the LDAP groups DN. -+.Pp -+LpkGroupDN ou=groups,dc=phear,dc=org -+.It Cm LpkBindDN -+Specifies the LDAP bind DN to use if necessary. -+.Pp -+LpkBindDN cn=Manager,dc=phear,dc=org -+.It Cm LpkBindPw -+Specifies the LDAP bind credential. -+.Pp -+LpkBindPw secret -+.It Cm LpkServerGroup -+Specifies one or more [:space:] separated group the server is part of. -+.Pp -+LpkServerGroup unix mail prod -+.It Cm LpkForceTLS -+Specifies if the LDAP server connection must be tried, forced or not used. The argument must be -+.Dq yes -+or -+.Dq no -+or -+.Dq try . -+.It Cm LpkSearchTimelimit -+Sepcifies the search time limit before the search is considered over. value is -+in seconds. -+.Pp -+LpkSearchTimelimit 3 -+.It Cm LpkBindTimelimit -+Sepcifies the bind time limit before the connection is considered dead. value is -+in seconds. -+.Pp -+LpkBindTimelimit 3 - .El - .Sh TIME FORMATS - .Xr sshd 8 |