diff -Nru vanilla/4.6p1/Makefile.in lpk/4.6p1/Makefile.in --- vanilla/4.6p1/Makefile.in 2006-10-23 22:44:47.000000000 +0100 +++ lpk/4.6p1/Makefile.in 2007-07-30 11:24:01.000000000 +0100 @@ -89,7 +89,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 diff -Nru vanilla/4.6p1/README.lpk lpk/4.6p1/README.lpk --- vanilla/4.6p1/README.lpk 1970-01-01 01:00:00.000000000 +0100 +++ lpk/4.6p1/README.lpk 2007-07-30 11:24:01.000000000 +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¸ 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://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 + - Andrea Barisani diff -Nru vanilla/4.6p1/auth-rsa.c lpk/4.6p1/auth-rsa.c --- vanilla/4.6p1/auth-rsa.c 2006-11-07 12:14:42.000000000 +0000 +++ lpk/4.6p1/auth-rsa.c 2007-07-30 11:24:01.000000000 +0100 @@ -175,10 +175,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); diff -Nru vanilla/4.6p1/auth2-pubkey.c lpk/4.6p1/auth2-pubkey.c --- vanilla/4.6p1/auth2-pubkey.c 2006-08-05 03:39:39.000000000 +0100 +++ lpk/4.6p1/auth2-pubkey.c 2007-07-30 11:24:01.000000000 +0100 @@ -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 */ diff -Nru vanilla/4.6p1/config.h.in lpk/4.6p1/config.h.in --- vanilla/4.6p1/config.h.in 2007-03-06 10:39:55.000000000 +0000 +++ lpk/4.6p1/config.h.in 2007-07-30 11:24:01.000000000 +0100 @@ -510,6 +510,9 @@ /* Define to 1 if you have the 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 diff -Nru vanilla/4.6p1/configure lpk/4.6p1/configure --- vanilla/4.6p1/configure 2007-08-01 16:48:31.000000000 +0100 +++ lpk/4.6p1/configure 2007-08-01 16:49:48.000000000 +0100 @@ -1341,6 +1341,7 @@ --with-tcp-wrappers[=PATH] Enable tcpwrappers support (optionally in PATH) --with-libedit[=PATH] Enable libedit support for sftp --with-audit=module Enable EXPERIMENTAL audit support (modules=debug,bsm) + --with-ldap[=PATH] Enable LDAP pubkey support (optionally in PATH) --with-ssl-dir=PATH Specify path to OpenSSL installation --without-openssl-header-check Disable OpenSSL version consistency check --with-ssl-engine Enable OpenSSL (hardware) ENGINE support @@ -12283,6 +12284,85 @@ 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 + + +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 + #include +int +main () +{ +(void)ldap_init(0, 0); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&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); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; 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 core conftest.err conftest.$ac_objext conftest.$ac_ext + fi + + +fi + + @@ -29333,6 +29413,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" diff -Nru vanilla/4.6p1/configure.ac lpk/4.6p1/configure.ac --- vanilla/4.6p1/configure.ac 2007-03-05 00:51:27.000000000 +0000 +++ lpk/4.6p1/configure.ac 2007-07-30 11:24:01.000000000 +0100 @@ -1218,6 +1218,37 @@ esac ] ) +# 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 + #include ], + [(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 \ @@ -3986,6 +4017,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" diff -Nru vanilla/4.6p1/ldapauth.c lpk/4.6p1/ldapauth.c --- vanilla/4.6p1/ldapauth.c 1970-01-01 01:00:00.000000000 +0100 +++ lpk/4.6p1/ldapauth.c 2007-07-31 12:08:36.000000000 +0100 @@ -0,0 +1,575 @@ +/* + * $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 + * 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 +#include +#include +#include + +#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) + */ + +/* +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) (size_t) (strlen(uid)+64+(filter != NULL ? strlen(filter) : 0)) +#define REQUEST_USER(buffer, pwname, customfilter) \ + buffer = (char *) calloc(REQUEST_USER_SIZE(pwname, customfilter), sizeof(char)); \ + if (!buffer) { \ + perror("calloc()"); \ + return NULL; \ + } \ + snprintf(buffer, REQUEST_USER_SIZE(pwname, customfilter), \ + "(&(objectclass=posixAccount)(objectclass=ldapPublicKey)(uid=%s)%s)", \ + 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); +} + +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); + 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; + + 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); + + 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, 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, 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 */ diff -Nru vanilla/4.6p1/ldapauth.h lpk/4.6p1/ldapauth.h --- vanilla/4.6p1/ldapauth.h 1970-01-01 01:00:00.000000000 +0100 +++ lpk/4.6p1/ldapauth.h 2007-07-30 14:58:04.000000000 +0100 @@ -0,0 +1,124 @@ +/* + * $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 + * 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 +#include +#include +#include + +/* 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" + +/* 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 + +/* 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 * 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?) */ +} 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 diff -Nru vanilla/4.6p1/lpk-user-example.txt lpk/4.6p1/lpk-user-example.txt --- vanilla/4.6p1/lpk-user-example.txt 1970-01-01 01:00:00.000000000 +0100 +++ lpk/4.6p1/lpk-user-example.txt 2007-07-30 11:24:01.000000000 +0100 @@ -0,0 +1,117 @@ + +Post to ML -> User Made Quick Install Doc. +Contribution from John Lane + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +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 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. + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ diff -Nru vanilla/4.6p1/openssh-lpk_openldap.schema lpk/4.6p1/openssh-lpk_openldap.schema --- vanilla/4.6p1/openssh-lpk_openldap.schema 1970-01-01 01:00:00.000000000 +0100 +++ lpk/4.6p1/openssh-lpk_openldap.schema 2007-07-30 11:24:01.000000000 +0100 @@ -0,0 +1,19 @@ +# +# LDAP Public Key Patch schema for use with openssh-ldappubkey +# Author: Eric AUGE +# +# 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 ) + ) diff -Nru vanilla/4.6p1/openssh-lpk_sun.schema lpk/4.6p1/openssh-lpk_sun.schema --- vanilla/4.6p1/openssh-lpk_sun.schema 1970-01-01 01:00:00.000000000 +0100 +++ lpk/4.6p1/openssh-lpk_sun.schema 2007-07-30 11:24:01.000000000 +0100 @@ -0,0 +1,21 @@ +# +# LDAP Public Key Patch schema for use with openssh-ldappubkey +# Author: Eric AUGE +# +# 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 ) + ) diff -Nru vanilla/4.6p1/servconf.c lpk/4.6p1/servconf.c --- vanilla/4.6p1/servconf.c 2007-03-01 10:31:29.000000000 +0000 +++ lpk/4.6p1/servconf.c 2007-07-30 15:12:07.000000000 +0100 @@ -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); @@ -122,6 +126,25 @@ options->permit_tun = -1; options->num_permitted_opens = -1; options->adm_forced_command = 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; +#endif + } void @@ -249,6 +272,32 @@ 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.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; +#endif /* Turn privilege separation on by default */ if (use_privsep == -1) @@ -294,6 +343,12 @@ sMatch, sPermitOpen, sForceCommand, sUsePrivilegeSeparation, sDeprecated, sUnsupported +#ifdef WITH_LDAP_PUBKEY + ,sLdapPublickey, sLdapServers, sLdapUserDN + ,sLdapGroupDN, sBindDN, sBindPw, sMyGroup + ,sLdapFilter, sForceTLS, sBindTimeout + ,sSearchTimeout, sLdapConf +#endif } ServerOpCodes; #define SSHCFG_GLOBAL 0x01 /* allowed in main section of sshd_config */ @@ -397,6 +452,20 @@ { "clientalivecountmax", sClientAliveCountMax, SSHCFG_GLOBAL }, { "authorizedkeysfile", sAuthorizedKeysFile, SSHCFG_GLOBAL }, { "authorizedkeysfile2", sAuthorizedKeysFile2, SSHCFG_GLOBAL }, +#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_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 }, +#endif { "useprivilegeseparation", sUsePrivilegeSeparation, SSHCFG_GLOBAL }, { "acceptenv", sAcceptEnv, SSHCFG_GLOBAL }, { "permittunnel", sPermitTunnel, SSHCFG_GLOBAL }, @@ -1267,6 +1336,107 @@ 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 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: + 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 = 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; +#endif default: fatal("%s line %d: Missing handler for opcode %s (%d)", diff -Nru vanilla/4.6p1/servconf.h lpk/4.6p1/servconf.h --- vanilla/4.6p1/servconf.h 2007-02-19 11:25:38.000000000 +0000 +++ lpk/4.6p1/servconf.h 2007-07-30 11:24:01.000000000 +0100 @@ -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,9 @@ int use_pam; /* Enable auth via PAM */ int permit_tun; +#ifdef WITH_LDAP_PUBKEY + ldap_opt_t lpk; +#endif int num_permitted_opens; } ServerOptions; diff -Nru vanilla/4.6p1/sshd.c lpk/4.6p1/sshd.c --- vanilla/4.6p1/sshd.c 2007-02-25 09:37:22.000000000 +0000 +++ lpk/4.6p1/sshd.c 2007-07-30 11:24:01.000000000 +0100 @@ -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 @@ -1430,6 +1434,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 if required. */ diff -Nru vanilla/4.6p1/sshd_config lpk/4.6p1/sshd_config --- vanilla/4.6p1/sshd_config 2006-07-24 05:06:47.000000000 +0100 +++ lpk/4.6p1/sshd_config 2007-07-30 11:24:01.000000000 +0100 @@ -101,6 +101,21 @@ # 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 +#LpkFilter (hostAccess=master.phear.org) +#LpkForceTLS no +#LpkSearchTimelimit 3 +#LpkBindTimelimit 3 # override default of no subsystems Subsystem sftp /usr/libexec/sftp-server diff -Nru vanilla/4.6p1/sshd_config.5 lpk/4.6p1/sshd_config.5 --- vanilla/4.6p1/sshd_config.5 2007-03-06 10:21:18.000000000 +0000 +++ lpk/4.6p1/sshd_config.5 2007-07-30 11:24:01.000000000 +0100 @@ -902,6 +902,62 @@ 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 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