You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1811 lines
58 KiB

Index: Makefile.in
--- Makefile.in.orig 2021-03-02 11:31:47.000000000 +0100
+++ Makefile.in 2021-03-03 08:26:38.162144000 +0100
@@ -128,7 +128,7 @@
srclimit.o sftp-server.o sftp-common.o \
sandbox-null.o sandbox-rlimit.o sandbox-systrace.o sandbox-darwin.o \
sandbox-seccomp-filter.o sandbox-capsicum.o sandbox-pledge.o \
- sandbox-solaris.o uidswap.o $(SKOBJS)
+ sandbox-solaris.o uidswap.o ldapauth.o $(SKOBJS)
SCP_OBJS= scp.o progressmeter.o
Index: README.lpk
--- README.lpk.orig 2021-03-03 08:26:38.162489000 +0100
+++ README.lpk 2021-03-03 08:26:38.162407000 +0100
@@ -0,0 +1,267 @@
+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<EFBFBD> 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.
+
+<EFBFBD> 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://dev.inversepath.com/trac/openssh-lpk
+ http://lam.sf.net/ ( http://lam.sourceforge.net/documentation/supportedSchemas.htm )
+
+- CONTRIBUTORS/IDEAS/GREETS :
+ - Falk Siemonsmeier.
+ - Jacob Rief.
+ - Michael Durchgraf.
+ - frederic peters.
+ - Finlay dobbie.
+ - Stefan Fisher.
+ - Robin H. Johnson.
+ - Adrian Bridgett.
+
+- CONTACT :
+ - Eric AUGE <eau@phear.org>
+ - Andrea Barisani <andrea@inversepath.com>
Index: auth2-pubkey.c
--- auth2-pubkey.c.orig 2021-03-02 11:31:47.000000000 +0100
+++ auth2-pubkey.c 2021-03-03 08:26:38.162683000 +0100
@@ -71,6 +71,10 @@
#include "session.h" /* XXX for child_set_env(); refactor? */
#include "sk-api.h"
+#ifdef WITH_LDAP_PUBKEY
+#include "ldapauth.h"
+#endif
+
/* import */
extern ServerOptions options;
@@ -718,10 +722,76 @@
size_t linesize = 0;
int found_key = 0;
u_long linenum = 0;
+#ifdef WITH_LDAP_PUBKEY
+ ldap_key_t * k;
+ unsigned int i = 0;
+#endif
if (authoptsp != NULL)
*authoptsp = NULL;
+#ifdef WITH_LDAP_PUBKEY
+ /* 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);
+ free(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
+
while (getline(&line, &linesize, f) != -1) {
linenum++;
/* Always consume entire file */
Index: config.h.in
--- config.h.in.orig 2021-03-02 13:05:41.000000000 +0100
+++ config.h.in 2021-03-03 08:26:38.162979000 +0100
@@ -903,6 +903,9 @@
/* Define to 1 if you have the `localtime_r' function. */
#undef HAVE_LOCALTIME_R
+/* Define if you want LDAP support */
+#undef WITH_LDAP_PUBKEY
+
/* Define to 1 if you have the `login' function. */
#undef HAVE_LOGIN
Index: configure.ac
--- configure.ac.orig 2021-03-02 11:31:47.000000000 +0100
+++ configure.ac 2021-03-03 08:26:38.163566000 +0100
@@ -1782,6 +1782,37 @@
CFLAGS="$SAVED_CFLAGS"
AC_SUBST([PICFLAG])
+# Check whether user wants LDAP support
+LDAP_MSG="no"
+AC_ARG_WITH(ldap,
+ [ --with-ldap[[=PATH]] Enable LDAP pubkey 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], 1, [Enable LDAP pubkey support])
+ 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([ \
Blowfish_initstate \
@@ -5500,6 +5531,7 @@
echo " OSF SIA support: $SIA_MSG"
echo " KerberosV support: $KRB5_MSG"
echo " SELinux support: $SELINUX_MSG"
+echo " LDAP support: $LDAP_MSG"
echo " MD5 password support: $MD5_MSG"
echo " libedit support: $LIBEDIT_MSG"
echo " libldns support: $LDNS_MSG"
Index: configure
--- configure.orig 2021-03-02 13:05:37.000000000 +0100
+++ configure 2021-03-03 08:26:38.165184000 +0100
@@ -1466,6 +1466,7 @@
--with-ldns[=PATH] Use ldns for DNSSEC support (optionally in PATH)
--with-libedit[=PATH] Enable libedit support for sftp
--with-audit=module Enable audit support (modules=debug,bsm,linux)
+ --with-ldap[=PATH] Enable LDAP pubkey support (optionally in PATH)
--with-pie Build Position Independent Executables if possible
--with-security-key-builtin include builtin U2F/FIDO support
--with-ssl-dir=PATH Specify path to OpenSSL installation
@@ -14521,6 +14522,57 @@
fi
+# Check whether user wants LDAP support
+LDAP_MSG="no"
+
+# Check whether --with-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
+
+
+$as_echo "#define WITH_LDAP_PUBKEY 1" >>confdefs.h
+
+ LIBS="-lldap $LIBS"
+ LDAP_MSG="yes"
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for LDAP support" >&5
+$as_echo_n "checking for LDAP support... " >&6; }
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+ #include <ldap.h>
+int
+main ()
+{
+(void)ldap_init(0, 0);
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+ as_fn_error $? "** Incomplete or missing ldap libraries **" "$LINENO" 5
+
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ fi
+
+
+fi
+
+
for ac_func in \
arc4random \
arc4random_buf \
@@ -21762,6 +21814,7 @@
echo " OSF SIA support: $SIA_MSG"
echo " KerberosV support: $KRB5_MSG"
echo " SELinux support: $SELINUX_MSG"
+echo " LDAP support: $LDAP_MSG"
echo " MD5 password support: $MD5_MSG"
echo " libedit support: $LIBEDIT_MSG"
echo " libldns support: $LDNS_MSG"
Index: ldapauth.c
--- ldapauth.c.orig 2021-03-03 08:26:38.165500000 +0100
+++ ldapauth.c 2021-03-03 08:26:38.165418000 +0100
@@ -0,0 +1,579 @@
+/*
+ * $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 <stdarg.h>
+
+#include "ldapauth.h"
+#include "log.h"
+
+/* 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)
+ */
+
+/*
+XXX stock upstream version without extra filter support
+#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)
+ */
+
+#define REQUEST_USER_SIZE(uid, filter, uid_attr) (size_t) (strlen(uid)+strlen(uid_attr)+61+(filter != NULL ? strlen(filter) : 0))
+#define REQUEST_USER(buffer, pwname, customfilter, uid_attr) \
+ buffer = (char *) calloc(REQUEST_USER_SIZE(pwname, customfilter, uid_attr), sizeof(char)); \
+ if (!buffer) { \
+ perror("calloc()"); \
+ return NULL; \
+ } \
+ snprintf(buffer, REQUEST_USER_SIZE(pwname, customfilter, uid_attr), \
+ "(&(objectclass=posixAccount)(objectclass=ldapPublicKey)(%s=%s)%s)", \
+ uid_attr, pwname, (customfilter != NULL ? customfilter : ""))
+
+/* some portable and working tokenizer, lame though */
+static int tokenize(char ** o, size_t size, char * input) {
+ unsigned int i = 0, num;
+ const 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(const char * input) {
+ const char * charset = " \t";
+ const 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(const 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(const 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]) ||
+ (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) {
+ debug("ldap options:");
+ debug("servers: %s", ldap->servers);
+ if (ldap->u_basedn)
+ debug("user basedn: %s", ldap->u_basedn);
+ if (ldap->g_basedn)
+ debug("group basedn: %s", ldap->g_basedn);
+ if (ldap->binddn)
+ debug("binddn: %s", ldap->binddn);
+ if (ldap->bindpw)
+ debug("bindpw: %s", ldap->bindpw);
+ if (ldap->sgroup)
+ debug("group: %s", ldap->sgroup);
+ if (ldap->filter)
+ debug("filter: %s", ldap->filter);
+ if (ldap->uid_attr)
+ debug("uid_attr: %s", ldap->uid_attr);
+}
+
+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->filter)
+ free(l->filter);
+ if (l->l_conf)
+ free(l->l_conf);
+ if (l->uid_attr)
+ free(l->uid_attr);
+ 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, const char * user) {
+ ldap_key_t * k = (ldap_key_t *) calloc (1, sizeof(ldap_key_t));
+ LDAPMessage *res, *e;
+ char * filter;
+ int i;
+ char *attrs[] = {
+ l->pub_key_attr,
+ NULL
+ };
+
+ 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;
+
+ /* quick check for attempts to be evil */
+ if ((strchr(user, '(') != NULL) || (strchr(user, ')') != NULL) ||
+ (strchr(user, '*') != NULL) || (strchr(user, '\\') != NULL))
+ return NULL;
+
+ /* build filter for LDAP request */
+ REQUEST_USER(filter, user, l->filter, l->uid_attr);
+
+ 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)
+ debug("[LDAP] duplicate entries, using the FIRST entry returned");
+
+ e = ldap_first_entry(l->ld, res);
+ k->keys = ldap_get_values_len(l->ld, e, l->pub_key_attr);
+ 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, const 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;
+
+ /* quick check for attempts to be evil */
+ if ((strchr(user, '(') != NULL) || (strchr(user, ')') != NULL) ||
+ (strchr(user, '*') != NULL) || (strchr(user, '\\') != NULL))
+ return FAILURE;
+
+ /* 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(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 */
Index: ldapauth.h
--- ldapauth.h.orig 2021-03-03 08:26:38.165661000 +0100
+++ ldapauth.h 2021-03-03 08:26:38.165581000 +0100
@@ -0,0 +1,130 @@
+/*
+ * $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
+
+#define LDAP_DEPRECATED 1
+
+#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_FIL_TOKEN "LpkFilter"
+#define _DEFAULT_TLS_TOKEN "LpkForceTLS"
+#define _DEFAULT_BTI_TOKEN "LpkBindTimelimit"
+#define _DEFAULT_STI_TOKEN "LpkSearchTimelimit"
+#define _DEFAULT_LDP_TOKEN "LpkLdapConf"
+#define _DEFAULT_UID_TOKEN "LpkUIDAttribute"
+
+#define _DEFAULT_PUB_TOKEN "LpkPubKeyAttr"
+
+/* 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_FILTER NULL
+#define _DEFAULT_LPK_TLS -1
+#define _DEFAULT_LPK_BTIMEOUT 10
+#define _DEFAULT_LPK_STIMEOUT 10
+#define _DEFAULT_LPK_LDP NULL
+#define _DEFAULT_LPK_PUB "sshPublicKey"
+#define _DEFAULT_LPK_UID "uid"
+
+/* 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
+
+/*
+ *
+ * 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 * uid_attr; /* LDAP attribute that represents uid, defaults to uid*/
+ char * binddn; /* binddn */
+ char * bindpw; /* bind password */
+ char * sgroup; /* server group */
+ char * fgroup; /* group filter */
+ char * filter; /* additional 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?) */
+ char * pub_key_attr; /* Pubkey-Attribute */
+} 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(const char *);
+char * ldap_parse_servers(const 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 *, const char *);
+int ldap_ismember(ldap_opt_t *, const char *);
+
+#endif
Index: lpk-user-example.txt
--- lpk-user-example.txt.orig 2021-03-03 08:26:38.165827000 +0100
+++ lpk-user-example.txt 2021-03-03 08:26:38.165745000 +0100
@@ -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.
+
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Index: openssh-lpk_openldap.schema
--- openssh-lpk_openldap.schema.orig 2021-03-03 08:26:38.165970000 +0100
+++ openssh-lpk_openldap.schema 2021-03-03 08:26:38.165890000 +0100
@@ -0,0 +1,19 @@
+#
+# 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.24552.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.24552.500.1.1.2.0 NAME 'ldapPublicKey' SUP top AUXILIARY
+ DESC 'MANDATORY: OpenSSH LPK objectclass'
+ MUST ( sshPublicKey $ uid )
+ )
Index: openssh-lpk_sun.schema
--- openssh-lpk_sun.schema.orig 2021-03-03 08:26:38.166114000 +0100
+++ openssh-lpk_sun.schema 2021-03-03 08:26:38.166034000 +0100
@@ -0,0 +1,21 @@
+#
+# LDAP Public Key Patch schema for use with openssh-ldappubkey
+# Author: Eric AUGE <eau@phear.org>
+#
+# Schema for Sun Directory Server.
+# Based on the original schema, modified by Stefan Fischer.
+#
+
+dn: cn=schema
+
+# octetString SYNTAX
+attributeTypes: ( 1.3.6.1.4.1.24552.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
+objectClasses: ( 1.3.6.1.4.1.24552.500.1.1.2.0 NAME 'ldapPublicKey' SUP top AUXILIARY
+ DESC 'MANDATORY: OpenSSH LPK objectclass'
+ MUST ( sshPublicKey $ uid )
+ )
Index: servconf.c
--- servconf.c.orig 2021-03-02 11:31:47.000000000 +0100
+++ servconf.c 2021-03-03 08:26:38.166425000 +0100
@@ -71,6 +71,10 @@
#include "myproposal.h"
#include "digest.h"
+#ifdef WITH_LDAP_PUBKEY
+#include "ldapauth.h"
+#endif
+
static void add_listen_addr(ServerOptions *, const char *,
const char *, int);
static void add_one_listen_addr(ServerOptions *, const char *,
@@ -155,6 +159,26 @@
options->num_allow_groups = 0;
options->num_deny_groups = 0;
options->ciphers = NULL;
+#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.filter = NULL;
+ options->lpk.fgroup = NULL;
+ options->lpk.l_conf = NULL;
+ options->lpk.tls = -1;
+ options->lpk.b_timeout.tv_sec = -1;
+ options->lpk.s_timeout.tv_sec = -1;
+ options->lpk.flags = FLAG_EMPTY;
+ options->lpk.pub_key_attr = NULL;
+ options->lpk.uid_attr = NULL;
+#endif
options->macs = NULL;
options->kex_algorithms = NULL;
options->ca_sign_algorithms = NULL;
@@ -442,6 +466,36 @@
options->expose_userauth_info = 0;
if (options->sk_provider == NULL)
options->sk_provider = xstrdup("internal");
+#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.uid_attr == NULL)
+ options->lpk.uid_attr = _DEFAULT_LPK_UID;
+ 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.filter == NULL)
+ options->lpk.filter = _DEFAULT_LPK_FILTER;
+ if (options->lpk.tls == -1)
+ options->lpk.tls = _DEFAULT_LPK_TLS;
+ if (options->lpk.b_timeout.tv_sec == -1)
+ options->lpk.b_timeout.tv_sec = _DEFAULT_LPK_BTIMEOUT;
+ if (options->lpk.s_timeout.tv_sec == -1)
+ options->lpk.s_timeout.tv_sec = _DEFAULT_LPK_STIMEOUT;
+ if (options->lpk.l_conf == NULL)
+ options->lpk.l_conf = _DEFAULT_LPK_LDP;
+ if (options->lpk.pub_key_attr == NULL)
+ options->lpk.pub_key_attr = _DEFAULT_LPK_PUB;
+#endif
assemble_algorithms(options);
@@ -521,6 +575,12 @@
sAllowStreamLocalForwarding, sFingerprintHash, sDisableForwarding,
sExposeAuthInfo, sRDomain, sPubkeyAuthOptions, sSecurityKeyProvider,
sDeprecated, sIgnore, sUnsupported
+#ifdef WITH_LDAP_PUBKEY
+ ,sLdapPublickey, sLdapServers, sLdapUserDN
+ ,sLdapGroupDN, sBindDN, sBindPw, sUIDAttribute, sMyGroup
+ ,sLdapFilter, sForceTLS, sBindTimeout
+ ,sSearchTimeout, sLdapConf ,sLpkPubKeyAttr
+#endif
} ServerOpCodes;
#define SSHCFG_GLOBAL 0x01 /* allowed in main section of config */
@@ -645,6 +705,22 @@
{ "clientalivecountmax", sClientAliveCountMax, SSHCFG_ALL },
{ "authorizedkeysfile", sAuthorizedKeysFile, SSHCFG_ALL },
{ "authorizedkeysfile2", sDeprecated, SSHCFG_ALL },
+#ifdef WITH_LDAP_PUBKEY
+ { _DEFAULT_LPK_TOKEN, sLdapPublickey, SSHCFG_GLOBAL },
+ { _DEFAULT_SRV_TOKEN, sLdapServers, SSHCFG_GLOBAL },
+ { _DEFAULT_USR_TOKEN, sLdapUserDN, SSHCFG_GLOBAL },
+ { _DEFAULT_GRP_TOKEN, sLdapGroupDN, SSHCFG_GLOBAL },
+ { _DEFAULT_UID_TOKEN, sUIDAttribute, SSHCFG_GLOBAL },
+ { _DEFAULT_BDN_TOKEN, sBindDN, SSHCFG_GLOBAL },
+ { _DEFAULT_BPW_TOKEN, sBindPw, SSHCFG_GLOBAL },
+ { _DEFAULT_MYG_TOKEN, sMyGroup, SSHCFG_GLOBAL },
+ { _DEFAULT_FIL_TOKEN, sLdapFilter, SSHCFG_GLOBAL },
+ { _DEFAULT_TLS_TOKEN, sForceTLS, SSHCFG_GLOBAL },
+ { _DEFAULT_BTI_TOKEN, sBindTimeout, SSHCFG_GLOBAL },
+ { _DEFAULT_STI_TOKEN, sSearchTimeout, SSHCFG_GLOBAL },
+ { _DEFAULT_LDP_TOKEN, sLdapConf, SSHCFG_GLOBAL },
+ { "LpkPubKeyAttr", sLpkPubKeyAttr, SSHCFG_GLOBAL },
+#endif
{ "useprivilegeseparation", sDeprecated, SSHCFG_GLOBAL},
{ "acceptenv", sAcceptEnv, SSHCFG_ALL },
{ "setenv", sSetEnv, SSHCFG_ALL },
@@ -1256,6 +1332,7 @@
int cmdline = 0, *intptr, value, value2, n, port, oactive, r, found;
SyslogFacility *log_facility_ptr;
LogLevel *log_level_ptr;
+ unsigned long lvalue, *longptr;
ServerOpCodes opcode;
u_int i, *uintptr, uvalue, flags = 0;
size_t len;
@@ -1283,6 +1360,7 @@
if (!arg || !*arg || *arg == '#')
return 0;
intptr = NULL;
+ longptr = NULL;
charptr = NULL;
opcode = parse_token(arg, filename, linenum, &flags);
@@ -2405,6 +2483,133 @@
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 = cp;
+ 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 = cp;
+ 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 sUIDAttribute:
+ arg = cp;
+ if (!arg || *arg == '\0')
+ fatal("%s line %d: missing uid_attr",filename,linenum);
+ arg[strlen(arg)] = '\0';
+ options->lpk.uid_attr = xstrdup(arg);
+ memset(arg,0,strlen(arg));
+ break;
+ case sBindDN:
+ arg = cp;
+ 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 = cp;
+ 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:
+ arg = cp;
+ 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 sLdapFilter:
+ arg = cp;
+ if (!arg || *arg == '\0')
+ fatal("%s line %d: missing filter",filename, linenum);
+ arg[strlen(arg)] = '\0';
+ options->lpk.filter = xstrdup(arg);
+ 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:
+ longptr = (unsigned long *) &options->lpk.b_timeout.tv_sec;
+parse_ulong:
+ arg = strdelim(&cp);
+ if (!arg || *arg == '\0')
+ fatal("%s line %d: missing integer value.",
+ filename, linenum);
+ lvalue = atol(arg);
+ if (*activep && *longptr == -1)
+ *longptr = lvalue;
+ break;
+
+ case sSearchTimeout:
+ longptr = (unsigned long *) &options->lpk.s_timeout.tv_sec;
+ goto parse_ulong;
+ break;
+ case sLdapConf:
+ arg = cp;
+ 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;
+ case sLpkPubKeyAttr:
+ arg = cp;
+ if (!arg || *arg == '\0')
+ fatal("%s line %d: missing pubkeyattr",filename,linenum);
+ arg[strlen(arg)] = '\0';
+ options->lpk.pub_key_attr = xstrdup(arg);
+ memset(arg,0,strlen(arg));
+ break;
+
+#endif
default:
fatal("%s line %d: Missing handler for opcode %s (%d)",
Index: servconf.h
--- servconf.h.orig 2021-03-02 11:31:47.000000000 +0100
+++ servconf.h 2021-03-03 08:26:38.166616000 +0100
@@ -18,6 +18,10 @@
#include <openbsd-compat/sys-queue.h>
+#ifdef WITH_LDAP_PUBKEY
+#include "ldapauth.h"
+#endif
+
#define MAX_PORTS 256 /* Max # ports. */
#define MAX_SUBSYSTEMS 256 /* Max # subsystems. */
@@ -202,6 +206,9 @@
int use_pam; /* Enable auth via PAM */
int permit_tun;
+#ifdef WITH_LDAP_PUBKEY
+ ldap_opt_t lpk;
+#endif
char **permitted_opens; /* May also be one of PERMITOPEN_* */
u_int num_permitted_opens;
Index: sshd.c
--- sshd.c.orig 2021-03-02 11:31:47.000000000 +0100
+++ sshd.c 2021-03-03 22:55:00.213357000 +0100
@@ -131,6 +131,10 @@
#define REEXEC_CONFIG_PASS_FD (STDERR_FILENO + 3)
#define REEXEC_MIN_FREE_FD (STDERR_FILENO + 4)
+#ifdef WITH_LDAP_PUBKEY
+#include "ldapauth.h"
+#endif
+
extern char *__progname;
/* Server configuration options. */
@@ -1766,6 +1770,17 @@
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 %s, %s", SSH_VERSION, SSH_OPENSSL_VERSION);
/* Store privilege separation user for later use if required. */
Index: sshd_config.5
--- sshd_config.5.orig 2021-03-02 11:31:47.000000000 +0100
+++ sshd_config.5 2021-03-03 08:26:38.167335000 +0100
@@ -1832,6 +1832,62 @@
to not use one.
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 LpkFilter
+Specifies an additional LDAP filter to use for finding SSH keys
+.Pp
+LpkFilter (hostAccess=master.phear.org)
+.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
Index: sshd_config
--- sshd_config.orig 2021-03-02 11:31:47.000000000 +0100
+++ sshd_config 2021-03-03 08:26:38.167506000 +0100
@@ -105,6 +105,22 @@
# no default banner path
#Banner none
+# 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
+#LpkFilter (hostAccess=master.phear.org)
+#LpkForceTLS no
+#LpkSearchTimelimit 3
+#LpkBindTimelimit 3
+#LpkPubKeyAttr sshPublicKey
+
# override default of no subsystems
Subsystem sftp /usr/libexec/sftp-server