aboutsummaryrefslogtreecommitdiffstats
path: root/security/openssh-portable/files
diff options
context:
space:
mode:
authormnag <mnag@FreeBSD.org>2007-03-13 06:13:18 +0800
committermnag <mnag@FreeBSD.org>2007-03-13 06:13:18 +0800
commit97884f4c2f58abfebb4adf4cf31bd813191f6917 (patch)
treefe9ceb8df8acf6db455cf992624bd24860767ca9 /security/openssh-portable/files
parent3319e6b1e8a2764652db053d1f3929229edb3fab (diff)
downloadfreebsd-ports-gnome-97884f4c2f58abfebb4adf4cf31bd813191f6917.tar.gz
freebsd-ports-gnome-97884f4c2f58abfebb4adf4cf31bd813191f6917.tar.zst
freebsd-ports-gnome-97884f4c2f58abfebb4adf4cf31bd813191f6917.zip
- Update OpenSSH to 4.6p1
- Update GSSKEX patch to 20061220 - Update HPN patch to hpn12v16 - Update LPK patch to 0.3.8
Diffstat (limited to 'security/openssh-portable/files')
-rw-r--r--security/openssh-portable/files/openssh-4.5p1-gsskex-20061220.patch2337
-rw-r--r--security/openssh-portable/files/openssh-lpk.patch1813
2 files changed, 2337 insertions, 1813 deletions
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