1 This is a forward-port of the OpenSSH LPK support patch.
3 It adds support for storing OpenSSH public keys in LDAP. It also supports
4 grouping of machines in the LDAP data to limit users to specific machines.
6 The latest homepage for the LPK project is:
7 http://code.google.com/p/openssh-lpk/
9 The 0.3.10 version of the patch includes a fix for 64-bit platforms, as
10 discovered by Gentoo, where the bind timeout and search timeout values were not
11 being parsed correctly: http://bugs.gentoo.org/210110
13 Forward-ported-from: openssh-lpk-5.1p1-0.3.9.patch
14 Signed-off-by: Robin H. Johnson <robbat2@gentoo.org>
16 diff -Nuar --exclude '*.orig' --exclude '*.rej' openssh-5.1p1.orig/auth2-pubkey.c openssh-5.1p1+lpk/auth2-pubkey.c
17 --- openssh-5.1p1.orig/auth2-pubkey.c 2008-07-03 19:54:25.000000000 -0700
18 +++ openssh-5.1p1+lpk/auth2-pubkey.c 2008-08-23 15:02:47.000000000 -0700
20 #include "monitor_wrap.h"
23 +#ifdef WITH_LDAP_PUBKEY
24 +#include "ldapauth.h"
28 extern ServerOptions options;
29 extern u_char *session_id2;
34 +#ifdef WITH_LDAP_PUBKEY
39 /* Temporarily use the user's uid. */
40 temporarily_use_uid(pw);
42 +#ifdef WITH_LDAP_PUBKEY
44 + /* allocate a new key type */
45 + found = key_new(key->type);
47 + /* first check if the options is enabled, then try.. */
48 + if (options.lpk.on) {
49 + debug("[LDAP] trying LDAP first uid=%s",pw->pw_name);
50 + if (ldap_ismember(&options.lpk, pw->pw_name) > 0) {
51 + if ((k = ldap_getuserkey(&options.lpk, pw->pw_name)) != NULL) {
52 + /* Skip leading whitespace, empty and comment lines. */
53 + for (i = 0 ; i < k->num ; i++) {
54 + /* dont forget if multiple keys to reset options */
55 + char *cp, *options = NULL;
57 + for (cp = (char *)k->keys[i]->bv_val; *cp == ' ' || *cp == '\t'; cp++)
59 + if (!*cp || *cp == '\n' || *cp == '#')
62 + if (key_read(found, &cp) != 1) {
63 + /* no key? check if there are options for this key */
65 + debug2("[LDAP] user_key_allowed: check options: '%s'", cp);
67 + for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) {
68 + if (*cp == '\\' && cp[1] == '"')
69 + cp++; /* Skip both */
70 + else if (*cp == '"')
73 + /* Skip remaining whitespace. */
74 + for (; *cp == ' ' || *cp == '\t'; cp++)
76 + if (key_read(found, &cp) != 1) {
77 + debug2("[LDAP] user_key_allowed: advance: '%s'", cp);
78 + /* still no key? advance to next line*/
83 + if (key_equal(found, key) &&
84 + auth_parse_options(pw, options, file, linenum) == 1) {
86 + debug("[LDAP] matching key found");
87 + fp = key_fingerprint(found, SSH_FP_MD5, SSH_FP_HEX);
88 + verbose("[LDAP] Found matching %s key: %s", key_type(found), fp);
90 + /* restoring memory */
98 + }/* end of LDAP for() */
100 + logit("[LDAP] no keys found for '%s'!", pw->pw_name);
103 + logit("[LDAP] '%s' is not in '%s'", pw->pw_name, options.lpk.sgroup);
107 debug("trying public key file %s", file);
108 f = auth_openkeyfile(file, pw, options.strict_modes);
110 diff -Nuar --exclude '*.orig' --exclude '*.rej' openssh-5.1p1.orig/auth-rsa.c openssh-5.1p1+lpk/auth-rsa.c
111 --- openssh-5.1p1.orig/auth-rsa.c 2008-07-02 05:37:30.000000000 -0700
112 +++ openssh-5.1p1+lpk/auth-rsa.c 2008-08-23 15:02:47.000000000 -0700
113 @@ -174,10 +174,96 @@
117 +#ifdef WITH_LDAP_PUBKEY
119 + unsigned int i = 0;
122 /* Temporarily use the user's uid. */
123 temporarily_use_uid(pw);
125 +#ifdef WITH_LDAP_PUBKEY
126 + /* here is the job */
127 + key = key_new(KEY_RSA1);
129 + if (options.lpk.on) {
130 + debug("[LDAP] trying LDAP first uid=%s", pw->pw_name);
131 + if ( ldap_ismember(&options.lpk, pw->pw_name) > 0) {
132 + if ( (k = ldap_getuserkey(&options.lpk, pw->pw_name)) != NULL) {
133 + for (i = 0 ; i < k->num ; i++) {
134 + char *cp, *options = NULL;
136 + for (cp = k->keys[i]->bv_val; *cp == ' ' || *cp == '\t'; cp++)
138 + if (!*cp || *cp == '\n' || *cp == '#')
142 + * Check if there are options for this key, and if so,
143 + * save their starting address and skip the option part
144 + * for now. If there are no options, set the starting
147 + if (*cp < '0' || *cp > '9') {
150 + for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) {
151 + if (*cp == '\\' && cp[1] == '"')
152 + cp++; /* Skip both */
153 + else if (*cp == '"')
159 + /* Parse the key from the line. */
160 + if (hostfile_read_key(&cp, &bits, key) == 0) {
161 + debug("[LDAP] line %d: non ssh1 key syntax", i);
164 + /* cp now points to the comment part. */
166 + /* Check if the we have found the desired key (identified by its modulus). */
167 + if (BN_cmp(key->rsa->n, client_n) != 0)
170 + /* check the real bits */
171 + if (bits != (unsigned int)BN_num_bits(key->rsa->n))
172 + logit("[LDAP] Warning: ldap, line %lu: keysize mismatch: "
173 + "actual %d vs. announced %d.", (unsigned long)i, BN_num_bits(key->rsa->n), bits);
175 + /* We have found the desired key. */
177 + * If our options do not allow this key to be used,
178 + * do not send challenge.
180 + if (!auth_parse_options(pw, options, "[LDAP]", (unsigned long) i))
183 + /* break out, this key is allowed */
186 + /* add the return stuff etc... */
187 + /* Restore the privileged uid. */
190 + /* return key if allowed */
191 + if (allowed && rkey != NULL)
200 + logit("[LDAP] no keys found for '%s'!", pw->pw_name);
203 + logit("[LDAP] '%s' is not in '%s'", pw->pw_name, options.lpk.sgroup);
207 /* The authorized keys. */
208 file = authorized_keys_file(pw);
209 debug("trying public RSA key file %s", file);
210 diff -Nuar --exclude '*.orig' --exclude '*.rej' openssh-5.1p1.orig/config.h.in openssh-5.1p1+lpk/config.h.in
211 --- openssh-5.1p1.orig/config.h.in 2008-07-21 01:30:49.000000000 -0700
212 +++ openssh-5.1p1+lpk/config.h.in 2008-08-23 15:02:47.000000000 -0700
214 /* Define to 1 if you have the <linux/if_tun.h> header file. */
215 #undef HAVE_LINUX_IF_TUN_H
217 +/* Define if you want LDAP support */
218 +#undef WITH_LDAP_PUBKEY
220 /* Define if your libraries define login() */
223 diff -Nuar --exclude '*.orig' --exclude '*.rej' openssh-5.1p1.orig/configure openssh-5.1p1+lpk/configure
224 --- openssh-5.1p1.orig/configure 2008-07-21 01:30:50.000000000 -0700
225 +++ openssh-5.1p1+lpk/configure 2008-08-23 15:02:47.000000000 -0700
226 @@ -1340,6 +1340,7 @@
227 --with-tcp-wrappers[=PATH] Enable tcpwrappers support (optionally in PATH)
228 --with-libedit[=PATH] Enable libedit support for sftp
229 --with-audit=module Enable EXPERIMENTAL audit support (modules=debug,bsm)
230 + --with-ldap[=PATH] Enable LDAP pubkey support (optionally in PATH)
231 --with-ssl-dir=PATH Specify path to OpenSSL installation
232 --without-openssl-header-check Disable OpenSSL version consistency check
233 --with-ssl-engine Enable OpenSSL (hardware) ENGINE support
234 @@ -12568,6 +12569,85 @@
238 +# Check whether user wants LDAP support
241 +# Check whether --with-ldap was given.
242 +if test "${with_ldap+set}" = set; then
243 + withval=$with_ldap;
244 + if test "x$withval" != "xno" ; then
246 + if test "x$withval" != "xyes" ; then
247 + CPPFLAGS="$CPPFLAGS -I${withval}/include"
248 + LDFLAGS="$LDFLAGS -L${withval}/lib"
252 +cat >>confdefs.h <<\_ACEOF
253 +#define WITH_LDAP_PUBKEY 1
256 + LIBS="-lldap $LIBS"
259 + { echo "$as_me:$LINENO: checking for LDAP support" >&5
260 +echo $ECHO_N "checking for LDAP support... $ECHO_C" >&6; }
261 + cat >conftest.$ac_ext <<_ACEOF
264 +cat confdefs.h >>conftest.$ac_ext
265 +cat >>conftest.$ac_ext <<_ACEOF
266 +/* end confdefs.h. */
267 +#include <sys/types.h>
272 +(void)ldap_init(0, 0);
277 +rm -f conftest.$ac_objext
278 +if { (ac_try="$ac_compile"
280 + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
281 + *) ac_try_echo=$ac_try;;
283 +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
284 + (eval "$ac_compile") 2>conftest.er1
286 + grep -v '^ *+' conftest.er1 >conftest.err
288 + cat conftest.err >&5
289 + echo "$as_me:$LINENO: \$? = $ac_status" >&5
290 + (exit $ac_status); } && {
291 + test -z "$ac_c_werror_flag" ||
292 + test ! -s conftest.err
293 + } && test -s conftest.$ac_objext; then
294 + { echo "$as_me:$LINENO: result: yes" >&5
295 +echo "${ECHO_T}yes" >&6; }
297 + echo "$as_me: failed program was:" >&5
298 +sed 's/^/| /' conftest.$ac_ext >&5
301 + { echo "$as_me:$LINENO: result: no" >&5
302 +echo "${ECHO_T}no" >&6; }
303 + { { echo "$as_me:$LINENO: error: ** Incomplete or missing ldap libraries **" >&5
304 +echo "$as_me: error: ** Incomplete or missing ldap libraries **" >&2;}
305 + { (exit 1); exit 1; }; }
310 +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
320 @@ -30135,6 +30215,7 @@
321 echo " Smartcard support: $SCARD_MSG"
322 echo " S/KEY support: $SKEY_MSG"
323 echo " TCP Wrappers support: $TCPW_MSG"
324 +echo " LDAP support: $LDAP_MSG"
325 echo " MD5 password support: $MD5_MSG"
326 echo " libedit support: $LIBEDIT_MSG"
327 echo " Solaris process contract support: $SPC_MSG"
328 diff -Nuar --exclude '*.orig' --exclude '*.rej' openssh-5.1p1.orig/configure.ac openssh-5.1p1+lpk/configure.ac
329 --- openssh-5.1p1.orig/configure.ac 2008-07-09 04:07:19.000000000 -0700
330 +++ openssh-5.1p1+lpk/configure.ac 2008-08-23 15:02:47.000000000 -0700
331 @@ -1299,6 +1299,37 @@
335 +# Check whether user wants LDAP support
338 + [ --with-ldap[[=PATH]] Enable LDAP pubkey support (optionally in PATH)],
340 + if test "x$withval" != "xno" ; then
342 + if test "x$withval" != "xyes" ; then
343 + CPPFLAGS="$CPPFLAGS -I${withval}/include"
344 + LDFLAGS="$LDFLAGS -L${withval}/lib"
347 + AC_DEFINE([WITH_LDAP_PUBKEY], 1, [Enable LDAP pubkey support])
348 + LIBS="-lldap $LIBS"
351 + AC_MSG_CHECKING([for LDAP support])
353 + [#include <sys/types.h>
354 + #include <ldap.h>],
355 + [(void)ldap_init(0, 0);],
356 + [AC_MSG_RESULT(yes)],
359 + AC_MSG_ERROR([** Incomplete or missing ldap libraries **])
366 dnl Checks for library functions. Please keep in alphabetical order
369 @@ -4137,6 +4168,7 @@
370 echo " Smartcard support: $SCARD_MSG"
371 echo " S/KEY support: $SKEY_MSG"
372 echo " TCP Wrappers support: $TCPW_MSG"
373 +echo " LDAP support: $LDAP_MSG"
374 echo " MD5 password support: $MD5_MSG"
375 echo " libedit support: $LIBEDIT_MSG"
376 echo " Solaris process contract support: $SPC_MSG"
377 diff -Nuar --exclude '*.orig' --exclude '*.rej' openssh-5.1p1.orig/ldapauth.c openssh-5.1p1+lpk/ldapauth.c
378 --- openssh-5.1p1.orig/ldapauth.c 1969-12-31 16:00:00.000000000 -0800
379 +++ openssh-5.1p1+lpk/ldapauth.c 2008-08-23 15:02:47.000000000 -0700
387 + * Copyright (c) 2005, Eric AUGE <eau@phear.org>
388 + * All rights reserved.
390 + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
392 + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
393 + * 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.
394 + * 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.
396 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
397 + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
398 + * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
399 + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
400 + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
401 + * 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.
406 +#include "includes.h"
408 +#ifdef WITH_LDAP_PUBKEY
415 +#include "ldapauth.h"
418 +static char *attrs[] = {
423 +/* filter building infos */
424 +#define FILTER_GROUP_PREFIX "(&(objectclass=posixGroup)"
425 +#define FILTER_OR_PREFIX "(|"
426 +#define FILTER_OR_SUFFIX ")"
427 +#define FILTER_CN_PREFIX "(cn="
428 +#define FILTER_CN_SUFFIX ")"
429 +#define FILTER_UID_FORMAT "(memberUid=%s)"
430 +#define FILTER_GROUP_SUFFIX ")"
431 +#define FILTER_GROUP_SIZE(group) (size_t) (strlen(group)+(ldap_count_group(group)*5)+52)
433 +/* just filter building stuff */
434 +#define REQUEST_GROUP_SIZE(filter, uid) (size_t) (strlen(filter)+strlen(uid)+1)
435 +#define REQUEST_GROUP(buffer, prefilter, pwname) \
436 + buffer = (char *) calloc(REQUEST_GROUP_SIZE(prefilter, pwname), sizeof(char)); \
438 + perror("calloc()"); \
441 + snprintf(buffer, REQUEST_GROUP_SIZE(prefilter,pwname), prefilter, pwname)
443 +XXX OLD group building macros
444 +#define REQUEST_GROUP_SIZE(grp, uid) (size_t) (strlen(grp)+strlen(uid)+46)
445 +#define REQUEST_GROUP(buffer,pwname,grp) \
446 + buffer = (char *) calloc(REQUEST_GROUP_SIZE(grp, pwname), sizeof(char)); \
448 + perror("calloc()"); \
451 + snprintf(buffer,REQUEST_GROUP_SIZE(grp,pwname),"(&(objectclass=posixGroup)(cn=%s)(memberUid=%s))",grp,pwname)
455 +XXX stock upstream version without extra filter support
456 +#define REQUEST_USER_SIZE(uid) (size_t) (strlen(uid)+64)
457 +#define REQUEST_USER(buffer, pwname) \
458 + buffer = (char *) calloc(REQUEST_USER_SIZE(pwname), sizeof(char)); \
460 + perror("calloc()"); \
463 + snprintf(buffer,REQUEST_USER_SIZE(pwname),"(&(objectclass=posixAccount)(objectclass=ldapPublicKey)(uid=%s))",pwname)
466 +#define REQUEST_USER_SIZE(uid, filter) (size_t) (strlen(uid)+64+(filter != NULL ? strlen(filter) : 0))
467 +#define REQUEST_USER(buffer, pwname, customfilter) \
468 + buffer = (char *) calloc(REQUEST_USER_SIZE(pwname, customfilter), sizeof(char)); \
470 + perror("calloc()"); \
473 + snprintf(buffer, REQUEST_USER_SIZE(pwname, customfilter), \
474 + "(&(objectclass=posixAccount)(objectclass=ldapPublicKey)(uid=%s)%s)", \
475 + pwname, (customfilter != NULL ? customfilter : ""))
477 +/* some portable and working tokenizer, lame though */
478 +static int tokenize(char ** o, size_t size, char * input) {
479 + unsigned int i = 0, num;
480 + const char * charset = " \t";
481 + char * ptr = input;
483 + /* leading white spaces are ignored */
484 + num = strspn(ptr, charset);
487 + while ((num = strcspn(ptr, charset))) {
499 +void ldap_close(ldap_opt_t * ldap) {
504 + if ( ldap_unbind_ext(ldap->ld, NULL, NULL) < 0)
505 + ldap_perror(ldap->ld, "ldap_unbind()");
508 + FLAG_SET_DISCONNECTED(ldap->flags);
514 +int ldap_connect(ldap_opt_t * ldap) {
515 + int version = LDAP_VERSION3;
517 + if (!ldap->servers)
520 + /* Connection Init and setup */
521 + ldap->ld = ldap_init(ldap->servers, LDAP_PORT);
523 + ldap_perror(ldap->ld, "ldap_init()");
527 + if ( ldap_set_option(ldap->ld, LDAP_OPT_PROTOCOL_VERSION, &version) != LDAP_OPT_SUCCESS) {
528 + ldap_perror(ldap->ld, "ldap_set_option(LDAP_OPT_PROTOCOL_VERSION)");
532 + /* Timeouts setup */
533 + if (ldap_set_option(ldap->ld, LDAP_OPT_NETWORK_TIMEOUT, &ldap->b_timeout) != LDAP_SUCCESS) {
534 + ldap_perror(ldap->ld, "ldap_set_option(LDAP_OPT_NETWORK_TIMEOUT)");
536 + if (ldap_set_option(ldap->ld, LDAP_OPT_TIMEOUT, &ldap->s_timeout) != LDAP_SUCCESS) {
537 + ldap_perror(ldap->ld, "ldap_set_option(LDAP_OPT_TIMEOUT)");
541 + if ( (ldap->tls == -1) || (ldap->tls == 1) ) {
542 + if (ldap_start_tls_s(ldap->ld, NULL, NULL ) != LDAP_SUCCESS) {
543 + /* failed then reinit the initial connect */
544 + ldap_perror(ldap->ld, "ldap_connect: (TLS) ldap_start_tls()");
545 + if (ldap->tls == 1)
548 + ldap->ld = ldap_init(ldap->servers, LDAP_PORT);
550 + ldap_perror(ldap->ld, "ldap_init()");
554 + if ( ldap_set_option(ldap->ld, LDAP_OPT_PROTOCOL_VERSION, &version) != LDAP_OPT_SUCCESS) {
555 + ldap_perror(ldap->ld, "ldap_set_option()");
562 + if ( ldap_simple_bind_s(ldap->ld, ldap->binddn, ldap->bindpw) != LDAP_SUCCESS) {
563 + ldap_perror(ldap->ld, "ldap_simple_bind_s()");
567 + /* says it is connected */
568 + FLAG_SET_CONNECTED(ldap->flags);
573 +/* must free allocated ressource */
574 +static char * ldap_build_host(char *host, int port) {
575 + unsigned int size = strlen(host)+11;
576 + char * h = (char *) calloc (size, sizeof(char));
581 + rc = snprintf(h, size, "%s:%d ", host, port);
587 +static int ldap_count_group(const char * input) {
588 + const char * charset = " \t";
589 + const char * ptr = input;
590 + unsigned int count = 0;
593 + num = strspn(ptr, charset);
596 + while ((num = strcspn(ptr, charset))) {
606 +char * ldap_parse_groups(const char * groups) {
607 + unsigned int buffer_size = FILTER_GROUP_SIZE(groups);
608 + char * buffer = (char *) calloc(buffer_size, sizeof(char));
611 + unsigned int i = 0;
613 + if ((!groups)||(!buffer))
616 + g = strdup(groups);
622 + /* first separate into n tokens */
623 + if ( tokenize(garray, sizeof(garray)/sizeof(*garray), g) < 0) {
629 + /* build the final filter format */
630 + strlcat(buffer, FILTER_GROUP_PREFIX, buffer_size);
631 + strlcat(buffer, FILTER_OR_PREFIX, buffer_size);
633 + while (garray[i]) {
634 + strlcat(buffer, FILTER_CN_PREFIX, buffer_size);
635 + strlcat(buffer, garray[i], buffer_size);
636 + strlcat(buffer, FILTER_CN_SUFFIX, buffer_size);
639 + strlcat(buffer, FILTER_OR_SUFFIX, buffer_size);
640 + strlcat(buffer, FILTER_UID_FORMAT, buffer_size);
641 + strlcat(buffer, FILTER_GROUP_SUFFIX, buffer_size);
647 +/* a bit dirty but leak free */
648 +char * ldap_parse_servers(const char * servers) {
650 + char * tmp = NULL, *urls[32];
651 + unsigned int num = 0 , i = 0 , asize = 0;
652 + LDAPURLDesc *urld[32];
657 + /* local copy of the arg */
658 + s = strdup(servers);
662 + /* first separate into URL tokens */
663 + if ( tokenize(urls, sizeof(urls)/sizeof(*urls), s) < 0)
668 + if (! ldap_is_ldap_url(urls[i]) ||
669 + (ldap_url_parse(urls[i], &urld[i]) != 0)) {
678 + /* how much memory do we need */
680 + for (i = 0 ; i < num ; i++)
681 + asize += strlen(urld[i]->lud_host)+11;
684 + s = (char *) calloc( asize+1 , sizeof(char));
686 + for (i = 0 ; i < num ; i++)
687 + ldap_free_urldesc(urld[i]);
691 + /* then build the final host string */
692 + for (i = 0 ; i < num ; i++) {
693 + /* built host part */
694 + tmp = ldap_build_host(urld[i]->lud_host, urld[i]->lud_port);
695 + strncat(s, tmp, strlen(tmp));
696 + ldap_free_urldesc(urld[i]);
703 +void ldap_options_print(ldap_opt_t * ldap) {
704 + debug("ldap options:");
705 + debug("servers: %s", ldap->servers);
706 + if (ldap->u_basedn)
707 + debug("user basedn: %s", ldap->u_basedn);
708 + if (ldap->g_basedn)
709 + debug("group basedn: %s", ldap->g_basedn);
711 + debug("binddn: %s", ldap->binddn);
713 + debug("bindpw: %s", ldap->bindpw);
715 + debug("group: %s", ldap->sgroup);
717 + debug("filter: %s", ldap->filter);
720 +void ldap_options_free(ldap_opt_t * l) {
745 +void ldap_keys_free(ldap_key_t * k) {
746 + ldap_value_free_len(k->keys);
751 +ldap_key_t * ldap_getuserkey(ldap_opt_t *l, const char * user) {
752 + ldap_key_t * k = (ldap_key_t *) calloc (1, sizeof(ldap_key_t));
753 + LDAPMessage *res, *e;
760 + /* Am i still connected ? RETRY n times */
761 + /* XXX TODO: setup some conf value for retrying */
762 + if (!(l->flags & FLAG_CONNECTED))
763 + for (i = 0 ; i < 2 ; i++)
764 + if (ldap_connect(l) == 0)
767 + /* quick check for attempts to be evil */
768 + if ((strchr(user, '(') != NULL) || (strchr(user, ')') != NULL) ||
769 + (strchr(user, '*') != NULL) || (strchr(user, '\\') != NULL))
772 + /* build filter for LDAP request */
773 + REQUEST_USER(filter, user, l->filter);
775 + if ( ldap_search_st( l->ld,
777 + LDAP_SCOPE_SUBTREE,
779 + attrs, 0, &l->s_timeout, &res ) != LDAP_SUCCESS) {
781 + ldap_perror(l->ld, "ldap_search_st()");
786 + /* XXX error on search, timeout etc.. close ask for reconnect */
795 + /* check if any results */
796 + i = ldap_count_entries(l->ld,res);
804 + debug("[LDAP] duplicate entries, using the FIRST entry returned");
806 + e = ldap_first_entry(l->ld, res);
807 + k->keys = ldap_get_values_len(l->ld, e, PUBKEYATTR);
808 + k->num = ldap_count_values_len(k->keys);
816 + 0 if user is NOT member of current server group
817 + 1 if user IS MEMBER of current server group
819 +int ldap_ismember(ldap_opt_t * l, const char * user) {
824 + if ((!l->sgroup) || !(l->g_basedn))
827 + /* Am i still connected ? RETRY n times */
828 + /* XXX TODO: setup some conf value for retrying */
829 + if (!(l->flags & FLAG_CONNECTED))
830 + for (i = 0 ; i < 2 ; i++)
831 + if (ldap_connect(l) == 0)
834 + /* quick check for attempts to be evil */
835 + if ((strchr(user, '(') != NULL) || (strchr(user, ')') != NULL) ||
836 + (strchr(user, '*') != NULL) || (strchr(user, '\\') != NULL))
839 + /* build filter for LDAP request */
840 + REQUEST_GROUP(filter, l->fgroup, user);
842 + if (ldap_search_st( l->ld,
844 + LDAP_SCOPE_SUBTREE,
846 + NULL, 0, &l->s_timeout, &res) != LDAP_SUCCESS) {
848 + ldap_perror(l->ld, "ldap_search_st()");
852 + /* XXX error on search, timeout etc.. close ask for reconnect */
860 + /* check if any results */
861 + if (ldap_count_entries(l->ld, res) > 0) {
871 + * ldap.conf simple parser
872 + * XXX TODO: sanity checks
874 + * - free the previous ldap_opt_before replacing entries
875 + * - free each necessary previously parsed elements
877 + * -1 on FAILURE, 0 on SUCCESS
879 +int ldap_parse_lconf(ldap_opt_t * l) {
880 + FILE * lcd; /* ldap.conf descriptor */
882 + char * s = NULL, * k = NULL, * v = NULL;
885 + lcd = fopen (l->l_conf, "r");
887 + /* debug("Cannot open %s", l->l_conf); */
888 + perror("ldap_parse_lconf()");
892 + while (fgets (buf, sizeof (buf), lcd) != NULL) {
894 + if (*buf == '\n' || *buf == '#')
899 + while (*v != '\0' && *v != ' ' && *v != '\t')
907 + while (*v == ' ' || *v == '\t')
910 + li = strlen (v) - 1;
911 + while (v[li] == ' ' || v[li] == '\t' || v[li] == '\n')
915 + if (!strcasecmp (k, "uri")) {
916 + if ((l->servers = ldap_parse_servers(v)) == NULL) {
917 + fatal("error in ldap servers");
922 + else if (!strcasecmp (k, "base")) {
923 + s = strchr (v, '?');
926 + l->u_basedn = malloc (len + 1);
927 + strncpy (l->u_basedn, v, len);
928 + l->u_basedn[len] = '\0';
930 + l->u_basedn = strdup (v);
933 + else if (!strcasecmp (k, "binddn")) {
934 + l->binddn = strdup (v);
936 + else if (!strcasecmp (k, "bindpw")) {
937 + l->bindpw = strdup (v);
939 + else if (!strcasecmp (k, "timelimit")) {
940 + l->s_timeout.tv_sec = atoi (v);
942 + else if (!strcasecmp (k, "bind_timelimit")) {
943 + l->b_timeout.tv_sec = atoi (v);
945 + else if (!strcasecmp (k, "ssl")) {
946 + if (!strcasecmp (v, "start_tls"))
955 +#endif /* WITH_LDAP_PUBKEY */
956 diff -Nuar --exclude '*.orig' --exclude '*.rej' openssh-5.1p1.orig/ldapauth.h openssh-5.1p1+lpk/ldapauth.h
957 --- openssh-5.1p1.orig/ldapauth.h 1969-12-31 16:00:00.000000000 -0800
958 +++ openssh-5.1p1+lpk/ldapauth.h 2008-08-23 15:02:47.000000000 -0700
966 + * Copyright (c) 2005, Eric AUGE <eau@phear.org>
967 + * All rights reserved.
969 + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
971 + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
972 + * 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.
973 + * 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.
975 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
976 + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
977 + * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
978 + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
979 + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
980 + * 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.
988 +#define LDAP_DEPRECATED 1
995 +/* tokens in use for config */
996 +#define _DEFAULT_LPK_TOKEN "UseLPK"
997 +#define _DEFAULT_SRV_TOKEN "LpkServers"
998 +#define _DEFAULT_USR_TOKEN "LpkUserDN"
999 +#define _DEFAULT_GRP_TOKEN "LpkGroupDN"
1000 +#define _DEFAULT_BDN_TOKEN "LpkBindDN"
1001 +#define _DEFAULT_BPW_TOKEN "LpkBindPw"
1002 +#define _DEFAULT_MYG_TOKEN "LpkServerGroup"
1003 +#define _DEFAULT_FIL_TOKEN "LpkFilter"
1004 +#define _DEFAULT_TLS_TOKEN "LpkForceTLS"
1005 +#define _DEFAULT_BTI_TOKEN "LpkBindTimelimit"
1006 +#define _DEFAULT_STI_TOKEN "LpkSearchTimelimit"
1007 +#define _DEFAULT_LDP_TOKEN "LpkLdapConf"
1009 +/* default options */
1010 +#define _DEFAULT_LPK_ON 0
1011 +#define _DEFAULT_LPK_SERVERS NULL
1012 +#define _DEFAULT_LPK_UDN NULL
1013 +#define _DEFAULT_LPK_GDN NULL
1014 +#define _DEFAULT_LPK_BINDDN NULL
1015 +#define _DEFAULT_LPK_BINDPW NULL
1016 +#define _DEFAULT_LPK_SGROUP NULL
1017 +#define _DEFAULT_LPK_FILTER NULL
1018 +#define _DEFAULT_LPK_TLS -1
1019 +#define _DEFAULT_LPK_BTIMEOUT 10
1020 +#define _DEFAULT_LPK_STIMEOUT 10
1021 +#define _DEFAULT_LPK_LDP NULL
1024 +#define FLAG_EMPTY 0x00000000
1025 +#define FLAG_CONNECTED 0x00000001
1028 +#define FLAG_SET_EMPTY(x) x&=(FLAG_EMPTY)
1029 +#define FLAG_SET_CONNECTED(x) x|=(FLAG_CONNECTED)
1030 +#define FLAG_SET_DISCONNECTED(x) x&=~(FLAG_CONNECTED)
1035 +#define PUBKEYATTR "sshPublicKey"
1039 + * defined files path
1040 + * (should be relocated to pathnames.h,
1041 + * if one day it's included within the tree)
1044 +#define _PATH_LDAP_CONFIG_FILE "/etc/ldap.conf"
1047 +typedef struct ldap_options {
1048 + int on; /* Use it or NOT */
1049 + LDAP * ld; /* LDAP file desc */
1050 + char * servers; /* parsed servers for ldaplib failover handling */
1051 + char * u_basedn; /* user basedn */
1052 + char * g_basedn; /* group basedn */
1053 + char * binddn; /* binddn */
1054 + char * bindpw; /* bind password */
1055 + char * sgroup; /* server group */
1056 + char * fgroup; /* group filter */
1057 + char * filter; /* additional filter */
1058 + char * l_conf; /* use ldap.conf */
1059 + int tls; /* TLS only */
1060 + struct timeval b_timeout; /* bind timeout */
1061 + struct timeval s_timeout; /* search timeout */
1062 + unsigned int flags; /* misc flags (reconnection, future use?) */
1065 +typedef struct ldap_keys {
1066 + struct berval ** keys; /* the public keys retrieved */
1067 + unsigned int num; /* number of keys */
1071 +/* function headers */
1072 +void ldap_close(ldap_opt_t *);
1073 +int ldap_connect(ldap_opt_t *);
1074 +char * ldap_parse_groups(const char *);
1075 +char * ldap_parse_servers(const char *);
1076 +void ldap_options_print(ldap_opt_t *);
1077 +void ldap_options_free(ldap_opt_t *);
1078 +void ldap_keys_free(ldap_key_t *);
1079 +int ldap_parse_lconf(ldap_opt_t *);
1080 +ldap_key_t * ldap_getuserkey(ldap_opt_t *, const char *);
1081 +int ldap_ismember(ldap_opt_t *, const char *);
1084 diff -Nuar --exclude '*.orig' --exclude '*.rej' openssh-5.1p1.orig/lpk-user-example.txt openssh-5.1p1+lpk/lpk-user-example.txt
1085 --- openssh-5.1p1.orig/lpk-user-example.txt 1969-12-31 16:00:00.000000000 -0800
1086 +++ openssh-5.1p1+lpk/lpk-user-example.txt 2008-08-23 15:02:47.000000000 -0700
1089 +Post to ML -> User Made Quick Install Doc.
1090 +Contribution from John Lane <john@lane.uk.net>
1092 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1094 +OpenSSH LDAP keystore Patch
1095 +===========================
1097 +NOTE: these notes are a transcript of a specific installation
1098 + they work for me, your specifics may be different!
1099 + from John Lane March 17th 2005 john@lane.uk.net
1101 +This is a patch to OpenSSH 4.0p1 to allow it to obtain users' public keys
1102 +from their LDAP record as an alternative to ~/.ssh/authorized_keys.
1104 +(Assuming here that necessary build stuff is in $BUILD)
1106 +cd $BUILD/openssh-4.0p1
1107 +patch -Np1 -i $BUILD/openssh-lpk-4.0p1-0.3.patch
1108 +mkdir -p /var/empty &&
1109 +./configure --prefix=/usr --sysconfdir=/etc/ssh \
1110 + --libexecdir=/usr/sbin --with-md5-passwords --with-pam \
1111 + --with-libs="-lldap" --with-cppflags="-DWITH_LDAP_PUBKEY"
1116 +Add the following config to /etc/ssh/ssh_config
1118 +LpkServers ldap://myhost.mydomain.com
1119 +LpkUserDN ou=People,dc=mydomain,dc=com
1121 +We need to tell sshd about the SSL keys during boot, as root's
1122 +environment does not exist at that time. Edit /etc/rc.d/init.d/sshd.
1123 +Change the startup code from this:
1124 + echo "Starting SSH Server..."
1125 + loadproc /usr/sbin/sshd
1128 + echo "Starting SSH Server..."
1129 + LDAPRC="/root/.ldaprc" loadproc /usr/sbin/sshd
1132 +Re-start the sshd daemon:
1133 +/etc/rc.d/init.d/sshd restart
1135 +Install the additional LDAP schema
1136 +cp $BUILD/openssh-lpk-0.2.schema /etc/openldap/schema/openssh.schema
1138 +Now add the openSSH LDAP schema to /etc/openldap/slapd.conf:
1139 +Add the following to the end of the existing block of schema includes
1140 +include /etc/openldap/schema/openssh.schema
1142 +Re-start the LDAP server:
1143 +/etc/rc.d/init.d/slapd restart
1145 +To add one or more public keys to a user, eg "testuser" :
1146 +ldapsearch -x -W -Z -LLL -b "uid=testuser,ou=People,dc=mydomain,dc=com" -D
1147 +"uid=testuser,ou=People,dc=mydomain,dc=com" > /tmp/testuser
1149 +append the following to this /tmp/testuser file
1150 +objectclass: ldapPublicKey
1151 +sshPublicKey: ssh-rsa
1152 +AAAAB3NzaC1yc2EAAAABJQAAAIB3dsrwqXqD7E4zYYrxwdDKBUQxKMioXy9pxFVai64kAPxjU9KS
1153 +qIo7QfkjslfsjflksjfldfkjsldfjLX/5zkzRmT28I5piGzunPv17S89z8XwSsuAoR1t86t+5dlI
1154 +7eZE/gVbn2UQkQq7+kdDTS2yXV6VnC52N/kKLG3ciBkBAw== General Purpose RSA Key
1157 +ldapmodify -x -D "uid=testuser,ou=People,dc=mydomain,dc=com" -W -f
1159 +Enter LDAP Password:
1160 +modifying entry "uid=testuser,ou=People,dc=mydomain,dc=com"
1161 +And check the modify is ok:
1162 +ldapsearch -x -W -Z -b "uid=testuser,ou=People,dc=mydomain,dc=com" -D
1163 +"uid=testuser,ou=People,dc=mydomain,dc=com"
1164 +Enter LDAP Password:
1168 +# base <uid=testuser,ou=People,dc=mydomain,dc=com> with scope sub
1169 +# filter: (objectclass=*)
1173 +# testuser, People, mydomain.com
1174 +dn: uid=testuser,ou=People,dc=mydomain,dc=com
1177 +objectClass: account
1178 +objectClass: posixAccount
1180 +objectClass: shadowAccount
1181 +objectClass: ldapPublicKey
1182 +shadowLastChange: 12757
1185 +loginShell: /bin/bash
1188 +homeDirectory: /home/testuser
1189 +userPassword:: e1NTSEF9UDgwV1hnM1VjUDRJK0k1YnFiL1d4ZUJObXlZZ3Z3UTU=
1190 +sshPublicKey: ssh-rsa
1191 +AAAAB3NzaC1yc2EAAAABJQAAAIB3dsrwqXqD7E4zYYrxwdDKBUQxKMioXy9pxFVai64kAPxjU9KSqIo7QfkjslfsjflksjfldfkjsldfjLX/5zkzRmT28I5piGzunPv17S89z
1192 +8XwSsuAoR1t86t+5dlI7eZE/gVbn2UQkQq7+kdDTS2yXV6VnC52N/kKLG3ciBkBAw== General Purpose RSA Key
1201 +Now start a ssh session to user "testuser" from usual ssh client (e.g.
1202 +puTTY). Login should succeed.
1204 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1205 diff -Nuar --exclude '*.orig' --exclude '*.rej' openssh-5.1p1.orig/Makefile.in openssh-5.1p1+lpk/Makefile.in
1206 --- openssh-5.1p1.orig/Makefile.in 2008-07-08 07:21:12.000000000 -0700
1207 +++ openssh-5.1p1+lpk/Makefile.in 2008-08-23 15:02:47.000000000 -0700
1210 auth2-gss.o gss-serv.o gss-serv-krb5.o \
1211 loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o \
1212 - audit.o audit-bsm.o platform.o sftp-server.o sftp-common.o
1213 + audit.o audit-bsm.o platform.o ldapauth.o sftp-server.o sftp-common.o
1215 MANPAGES = moduli.5.out scp.1.out ssh-add.1.out ssh-agent.1.out ssh-keygen.1.out ssh-keyscan.1.out ssh.1.out sshd.8.out sftp-server.8.out sftp.1.out ssh-rand-helper.8.out ssh-keysign.8.out sshd_config.5.out ssh_config.5.out
1216 MANPAGES_IN = moduli.5 scp.1 ssh-add.1 ssh-agent.1 ssh-keygen.1 ssh-keyscan.1 ssh.1 sshd.8 sftp-server.8 sftp.1 ssh-rand-helper.8 ssh-keysign.8 sshd_config.5 ssh_config.5
1217 diff -Nuar --exclude '*.orig' --exclude '*.rej' openssh-5.1p1.orig/openssh-lpk_openldap.schema openssh-5.1p1+lpk/openssh-lpk_openldap.schema
1218 --- openssh-5.1p1.orig/openssh-lpk_openldap.schema 1969-12-31 16:00:00.000000000 -0800
1219 +++ openssh-5.1p1+lpk/openssh-lpk_openldap.schema 2008-08-23 15:02:47.000000000 -0700
1222 +# LDAP Public Key Patch schema for use with openssh-ldappubkey
1223 +# Author: Eric AUGE <eau@phear.org>
1225 +# Based on the proposal of : Mark Ruijter
1229 +# octetString SYNTAX
1230 +attributetype ( 1.3.6.1.4.1.24552.500.1.1.1.13 NAME 'sshPublicKey'
1231 + DESC 'MANDATORY: OpenSSH Public key'
1232 + EQUALITY octetStringMatch
1233 + SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )
1235 +# printableString SYNTAX yes|no
1236 +objectclass ( 1.3.6.1.4.1.24552.500.1.1.2.0 NAME 'ldapPublicKey' SUP top AUXILIARY
1237 + DESC 'MANDATORY: OpenSSH LPK objectclass'
1238 + MUST ( sshPublicKey $ uid )
1240 diff -Nuar --exclude '*.orig' --exclude '*.rej' openssh-5.1p1.orig/openssh-lpk_sun.schema openssh-5.1p1+lpk/openssh-lpk_sun.schema
1241 --- openssh-5.1p1.orig/openssh-lpk_sun.schema 1969-12-31 16:00:00.000000000 -0800
1242 +++ openssh-5.1p1+lpk/openssh-lpk_sun.schema 2008-08-23 15:02:47.000000000 -0700
1245 +# LDAP Public Key Patch schema for use with openssh-ldappubkey
1246 +# Author: Eric AUGE <eau@phear.org>
1248 +# Schema for Sun Directory Server.
1249 +# Based on the original schema, modified by Stefan Fischer.
1254 +# octetString SYNTAX
1255 +attributeTypes: ( 1.3.6.1.4.1.24552.500.1.1.1.13 NAME 'sshPublicKey'
1256 + DESC 'MANDATORY: OpenSSH Public key'
1257 + EQUALITY octetStringMatch
1258 + SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )
1260 +# printableString SYNTAX yes|no
1261 +objectClasses: ( 1.3.6.1.4.1.24552.500.1.1.2.0 NAME 'ldapPublicKey' SUP top AUXILIARY
1262 + DESC 'MANDATORY: OpenSSH LPK objectclass'
1263 + MUST ( sshPublicKey $ uid )
1265 diff -Nuar --exclude '*.orig' --exclude '*.rej' openssh-5.1p1.orig/README.lpk openssh-5.1p1+lpk/README.lpk
1266 --- openssh-5.1p1.orig/README.lpk 1969-12-31 16:00:00.000000000 -0800
1267 +++ openssh-5.1p1+lpk/README.lpk 2008-08-23 15:02:47.000000000 -0700
1269 +OpenSSH LDAP PUBLIC KEY PATCH
1270 +Copyright (c) 2003 Eric AUGE (eau@phear.org)
1271 +All rights reserved.
1273 +Redistribution and use in source and binary forms, with or without
1274 +modification, are permitted provided that the following conditions
1276 +1. Redistributions of source code must retain the above copyright
1277 + notice, this list of conditions and the following disclaimer.
1278 +2. Redistributions in binary form must reproduce the above copyright
1279 + notice, this list of conditions and the following disclaimer in the
1280 + documentation and/or other materials provided with the distribution.
1281 +3. The name of the author may not be used to endorse or promote products
1282 + derived from this software without specific prior written permission.
1284 +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1285 +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1286 +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1287 +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1288 +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
1289 +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
1290 +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
1291 +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1292 +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
1293 +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1295 +purposes of this patch:
1297 +This patch would help to have authentication centralization policy
1298 +using ssh public key authentication.
1299 +This patch could be an alternative to other "secure" authentication system
1300 +working in a similar way (Kerberos, SecurID, etc...), except the fact
1301 +that it's based on OpenSSH and its public key abilities.
1304 +'uid': means unix accounts existing on the current server
1305 +'lpkServerGroup:' mean server group configured on the current server ('lpkServerGroup' in sshd_config)
1310 + server1 (uid: eau,rival,toto) (lpkServerGroup: unix)
1312 + / \ --- - server3 (uid: eau, titi) (lpkServerGroup: unix)
1314 + | eau ,rival | server2 (uid: rival, eau) (lpkServerGroup: unix)
1316 + | userx,.... | server5 (uid: eau) (lpkServerGroup: mail)
1318 + ----- - server4 (uid: eau, rival) (no group configured)
1324 + * configured LDAP server somewhere on the network (i.e. OpenLDAP)
1325 + * patched sshd (with this patch ;)
1326 + * LDAP user(/group) entry (look at users.ldif (& groups.ldif)):
1328 + - attached to the 'ldapPublicKey' objectclass
1329 + - attached to the 'posixAccount' objectclass
1330 + - with a filled 'sshPublicKey' attribute
1332 + dn: uid=eau,ou=users,dc=cuckoos,dc=net
1334 + objectclass: person
1335 + objectclass: organizationalPerson
1336 + objectclass: posixAccount
1337 + objectclass: ldapPublicKey
1338 + description: Eric AUGE Account
1339 + userPassword: blah
1345 + homeDirectory: /export/home/eau
1346 + sshPublicKey: ssh-dss AAAAB3...
1347 + sshPublicKey: ssh-dss AAAAM5...
1350 + - attached to the 'posixGroup' objectclass
1351 + - with a 'cn' groupname attribute
1352 + - with multiple 'memberUid' attributes filled with usernames allowed in this group
1355 + dn: cn=unix,ou=groups,dc=cuckoos,dc=net
1357 + objectclass: posixGroup
1358 + description: Unix based servers group
1369 + If a user wants to authenticate to log in a server the sshd, will first look for authentication method allowed (RSAauth,kerberos,etc..)
1370 + and if RSAauth and tickets based auth fails, it will fallback to standard password authentication (if enabled).
1373 + 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.
1374 + It will do an ldapsearch to get the public key directly from the LDAP instead of reading it from the server filesystem.
1375 + (usually in $HOME/.ssh/authorized_keys)
1377 + 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.
1378 + If it fails, it falls back on RSA auth files ($HOME/.ssh/authorized_keys), etc.. and finally to standard password authentication (if enabled).
1380 + 7 tokens are added to sshd_config :
1381 + # here is the new patched ldap related tokens
1382 + # entries in your LDAP must be posixAccount & strongAuthenticationUser & posixGroup
1383 + UseLPK yes # look the pub key into LDAP
1384 + LpkServers ldap://10.31.32.5/ ldap://10.31.32.4 ldap://10.31.32.3 # which LDAP server for users ? (URL format)
1385 + LpkUserDN ou=users,dc=foobar,dc=net # which base DN for users ?
1386 + LpkGroupDN ou=groups,dc=foobar,dc=net # which base DN for groups ?
1387 + LpkBindDN cn=manager,dc=foobar,dc=net # which bind DN ?
1388 + LpkBindPw asecret # bind DN credidentials
1389 + LpkServerGroup agroupname # the group the server is part of
1391 + 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
1392 + flaws you have to take care of.
1394 +- HOW TO INSERT A USER/KEY INTO AN LDAP ENTRY
1396 + * my way (there is plenty :)
1397 + - create ldif file (i.e. users.ldif)
1398 + - cat ~/.ssh/id_dsa.pub OR cat ~/.ssh/id_rsa.pub OR cat ~/.ssh/identity.pub
1399 + - my way in 4 steps :
1402 + # you add this to the user entry in the LDIF file :
1404 + objectclass: posixAccount
1405 + objectclass: ldapPublicKey
1407 + sshPubliKey: ssh-dss AAAABDh12DDUR2...
1410 + # insert your entry and you're done :)
1411 + ldapadd -D balblabla -w bleh < file.ldif
1413 + all standard options can be present in the 'sshPublicKey' attribute.
1417 + Simply because, i was looking for a way to centralize all sysadmins authentication, easily, without completely using LDAP
1418 + as authentication method (like pam_ldap etc..).
1420 + After looking into Kerberos, SecurID, and other centralized secure authentications systems, the use of RSA and LDAP to get
1421 + public key for authentication allows us to control who has access to which server (the user needs an account and to be in 'strongAuthenticationUser'
1422 + objectclass within LDAP and part of the group the SSH server is in).
1424 + 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
1425 + so each user can change it as much as he wants).
1427 + Blocking a user account can be done directly from the LDAP (if sshd is using RSAAuth + ldap only).
1430 + Entry in the LDAP server must respect 'posixAccount' and 'ldapPublicKey' which are defined in core.schema.
1431 + and the additionnal lpk.schema.
1433 + This patch could allow a smooth transition between standard auth (/etc/passwd) and complete LDAP based authentication
1434 + (pamldap, nss_ldap, etc..).
1436 + This can be an alternative to other (old?/expensive?) authentication methods (Kerberos/SecurID/..).
1438 + Referring to schema at the beginning of this file if user 'eau' is only in group 'unix'
1439 + 'eau' would ONLY access 'server1', 'server2', 'server3' AND 'server4' BUT NOT 'server5'.
1440 + If you then modify the LDAP 'mail' group entry to add 'memberUid: eau' THEN user 'eau' would be able
1441 + to log in 'server5' (i hope you got the idea, my english is bad :).
1443 + Each server's sshd is patched and configured to ask the public key and the group infos in the LDAP
1445 + When you want to allow a new user to have access to the server parc, you just add him an account on
1446 + your servers, you add his public key into his entry on the LDAP server, it's done.
1448 + Because sshds are looking public keys into the LDAP directly instead of a file ($HOME/.ssh/authorized_keys).
1450 + When the user needs to change his passphrase he can do it directly from his workstation by changing
1451 + his own key set lock passphrase, and all servers are automatically aware.
1453 + With a CAREFUL LDAP server configuration you could allow a user to add/delete/modify his own entry himself
1454 + so he can add/modify/delete himself his public key when needed.
1457 + LDAP must be well configured, getting the public key of some user is not a problem, but if anonymous LDAP
1458 + allow write to users dn, somebody could replace someuser's public key by its own and impersonate some
1459 + of your users in all your server farm be VERY CAREFUL.
1461 + MITM attack when sshd is requesting the public key, could lead to a compromise of your servers allowing login
1462 + as the impersonnated user.
1464 + If LDAP server is down then, fallback on passwd auth.
1466 + the ldap code part has not been well audited yet.
1468 +- LDAP USER ENTRY EXAMPLES (LDIF Format, look in users.ldif)
1470 + dn: uid=jdoe,ou=users,dc=foobar,dc=net
1472 + objectclass: person
1473 + objectclass: organizationalPerson
1474 + objectclass: posixAccount
1475 + objectclass: ldapPublicKey
1476 + description: My account
1482 + homeDirectory: /home/jdoe
1483 + sshPublicKey: ssh-dss AAAAB3NzaC1kc3MAAAEBAOvL8pREUg9wSy/8+hQJ54YF3AXkB0OZrXB....
1487 +- LDAP GROUP ENTRY EXAMPLES (LDIF Format, look in groups.ldif)
1489 + dn: cn=unix,ou=groups,dc=cuckoos,dc=net
1491 + objectclass: posixGroup
1492 + description: Unix based servers group
1502 +Multiple 'sshPublicKey' in a user entry are allowed, as well as multiple 'memberUid' attributes in a group entry
1505 + 1. Apply the patch
1506 + 2. ./configure --with-your-options --with-ldap=/prefix/to/ldap_libs_and_includes
1511 + I hope this could help, and i hope to be clear enough,, or give ideas. questions/comments/improvements are welcome.
1514 + Redesign differently.
1517 + http://pacsec.jp/core05/psj05-barisani-en.pdf
1518 + http://fritz.potsdam.edu/projects/openssh-lpk/
1519 + http://fritz.potsdam.edu/projects/sshgate/
1520 + http://dev.inversepath.com/trac/openssh-lpk
1521 + http://lam.sf.net/ ( http://lam.sourceforge.net/documentation/supportedSchemas.htm )
1523 +- CONTRIBUTORS/IDEAS/GREETS :
1524 + - Falk Siemonsmeier.
1526 + - Michael Durchgraf.
1527 + - frederic peters.
1530 + - Robin H. Johnson.
1531 + - Adrian Bridgett.
1534 + - Eric AUGE <eau@phear.org>
1535 + - Andrea Barisani <andrea@inversepath.com>
1536 diff -Nuar --exclude '*.orig' --exclude '*.rej' openssh-5.1p1.orig/servconf.c openssh-5.1p1+lpk/servconf.c
1537 --- openssh-5.1p1.orig/servconf.c 2008-07-03 20:51:12.000000000 -0700
1538 +++ openssh-5.1p1+lpk/servconf.c 2008-08-23 15:02:47.000000000 -0700
1540 #include "channels.h"
1541 #include "groupaccess.h"
1543 +#ifdef WITH_LDAP_PUBKEY
1544 +#include "ldapauth.h"
1547 static void add_listen_addr(ServerOptions *, char *, int);
1548 static void add_one_listen_addr(ServerOptions *, char *, int);
1550 @@ -127,6 +131,25 @@
1551 options->adm_forced_command = NULL;
1552 options->chroot_directory = NULL;
1553 options->zero_knowledge_password_authentication = -1;
1554 +#ifdef WITH_LDAP_PUBKEY
1556 + options->lpk.ld = NULL;
1557 + options->lpk.on = -1;
1558 + options->lpk.servers = NULL;
1559 + options->lpk.u_basedn = NULL;
1560 + options->lpk.g_basedn = NULL;
1561 + options->lpk.binddn = NULL;
1562 + options->lpk.bindpw = NULL;
1563 + options->lpk.sgroup = NULL;
1564 + options->lpk.filter = NULL;
1565 + options->lpk.fgroup = NULL;
1566 + options->lpk.l_conf = NULL;
1567 + options->lpk.tls = -1;
1568 + options->lpk.b_timeout.tv_sec = -1;
1569 + options->lpk.s_timeout.tv_sec = -1;
1570 + options->lpk.flags = FLAG_EMPTY;
1576 @@ -258,6 +281,32 @@
1577 options->permit_tun = SSH_TUNMODE_NO;
1578 if (options->zero_knowledge_password_authentication == -1)
1579 options->zero_knowledge_password_authentication = 0;
1580 +#ifdef WITH_LDAP_PUBKEY
1581 + if (options->lpk.on == -1)
1582 + options->lpk.on = _DEFAULT_LPK_ON;
1583 + if (options->lpk.servers == NULL)
1584 + options->lpk.servers = _DEFAULT_LPK_SERVERS;
1585 + if (options->lpk.u_basedn == NULL)
1586 + options->lpk.u_basedn = _DEFAULT_LPK_UDN;
1587 + if (options->lpk.g_basedn == NULL)
1588 + options->lpk.g_basedn = _DEFAULT_LPK_GDN;
1589 + if (options->lpk.binddn == NULL)
1590 + options->lpk.binddn = _DEFAULT_LPK_BINDDN;
1591 + if (options->lpk.bindpw == NULL)
1592 + options->lpk.bindpw = _DEFAULT_LPK_BINDPW;
1593 + if (options->lpk.sgroup == NULL)
1594 + options->lpk.sgroup = _DEFAULT_LPK_SGROUP;
1595 + if (options->lpk.filter == NULL)
1596 + options->lpk.filter = _DEFAULT_LPK_FILTER;
1597 + if (options->lpk.tls == -1)
1598 + options->lpk.tls = _DEFAULT_LPK_TLS;
1599 + if (options->lpk.b_timeout.tv_sec == -1)
1600 + options->lpk.b_timeout.tv_sec = _DEFAULT_LPK_BTIMEOUT;
1601 + if (options->lpk.s_timeout.tv_sec == -1)
1602 + options->lpk.s_timeout.tv_sec = _DEFAULT_LPK_STIMEOUT;
1603 + if (options->lpk.l_conf == NULL)
1604 + options->lpk.l_conf = _DEFAULT_LPK_LDP;
1607 /* Turn privilege separation on by default */
1608 if (use_privsep == -1)
1609 @@ -303,6 +352,12 @@
1610 sMatch, sPermitOpen, sForceCommand, sChrootDirectory,
1611 sUsePrivilegeSeparation, sAllowAgentForwarding,
1612 sDeprecated, sUnsupported
1613 +#ifdef WITH_LDAP_PUBKEY
1614 + ,sLdapPublickey, sLdapServers, sLdapUserDN
1615 + ,sLdapGroupDN, sBindDN, sBindPw, sMyGroup
1616 + ,sLdapFilter, sForceTLS, sBindTimeout
1617 + ,sSearchTimeout, sLdapConf
1621 #define SSHCFG_GLOBAL 0x01 /* allowed in main section of sshd_config */
1622 @@ -408,6 +463,20 @@
1623 { "clientalivecountmax", sClientAliveCountMax, SSHCFG_GLOBAL },
1624 { "authorizedkeysfile", sAuthorizedKeysFile, SSHCFG_GLOBAL },
1625 { "authorizedkeysfile2", sAuthorizedKeysFile2, SSHCFG_GLOBAL },
1626 +#ifdef WITH_LDAP_PUBKEY
1627 + { _DEFAULT_LPK_TOKEN, sLdapPublickey, SSHCFG_GLOBAL },
1628 + { _DEFAULT_SRV_TOKEN, sLdapServers, SSHCFG_GLOBAL },
1629 + { _DEFAULT_USR_TOKEN, sLdapUserDN, SSHCFG_GLOBAL },
1630 + { _DEFAULT_GRP_TOKEN, sLdapGroupDN, SSHCFG_GLOBAL },
1631 + { _DEFAULT_BDN_TOKEN, sBindDN, SSHCFG_GLOBAL },
1632 + { _DEFAULT_BPW_TOKEN, sBindPw, SSHCFG_GLOBAL },
1633 + { _DEFAULT_MYG_TOKEN, sMyGroup, SSHCFG_GLOBAL },
1634 + { _DEFAULT_FIL_TOKEN, sLdapFilter, SSHCFG_GLOBAL },
1635 + { _DEFAULT_TLS_TOKEN, sForceTLS, SSHCFG_GLOBAL },
1636 + { _DEFAULT_BTI_TOKEN, sBindTimeout, SSHCFG_GLOBAL },
1637 + { _DEFAULT_STI_TOKEN, sSearchTimeout, SSHCFG_GLOBAL },
1638 + { _DEFAULT_LDP_TOKEN, sLdapConf, SSHCFG_GLOBAL },
1640 { "useprivilegeseparation", sUsePrivilegeSeparation, SSHCFG_GLOBAL },
1641 { "acceptenv", sAcceptEnv, SSHCFG_GLOBAL },
1642 { "permittunnel", sPermitTunnel, SSHCFG_GLOBAL },
1643 @@ -1294,6 +1363,107 @@
1645 arg = strdelim(&cp);
1647 +#ifdef WITH_LDAP_PUBKEY
1648 + case sLdapPublickey:
1649 + intptr = &options->lpk.on;
1651 + case sLdapServers:
1652 + /* arg = strdelim(&cp); */
1656 + if (!arg || *arg == '\0')
1657 + fatal("%s line %d: missing ldap server",filename,linenum);
1658 + arg[strlen(arg)] = '\0';
1659 + if ((options->lpk.servers = ldap_parse_servers(arg)) == NULL)
1660 + fatal("%s line %d: error in ldap servers", filename, linenum);
1661 + memset(arg,0,strlen(arg));
1665 + if (!arg || *arg == '\0')
1666 + fatal("%s line %d: missing ldap server",filename,linenum);
1667 + arg[strlen(arg)] = '\0';
1668 + options->lpk.u_basedn = xstrdup(arg);
1669 + memset(arg,0,strlen(arg));
1671 + case sLdapGroupDN:
1673 + if (!arg || *arg == '\0')
1674 + fatal("%s line %d: missing ldap server",filename,linenum);
1675 + arg[strlen(arg)] = '\0';
1676 + options->lpk.g_basedn = xstrdup(arg);
1677 + memset(arg,0,strlen(arg));
1681 + if (!arg || *arg == '\0')
1682 + fatal("%s line %d: missing binddn",filename,linenum);
1683 + arg[strlen(arg)] = '\0';
1684 + options->lpk.binddn = xstrdup(arg);
1685 + memset(arg,0,strlen(arg));
1689 + if (!arg || *arg == '\0')
1690 + fatal("%s line %d: missing bindpw",filename,linenum);
1691 + arg[strlen(arg)] = '\0';
1692 + options->lpk.bindpw = xstrdup(arg);
1693 + memset(arg,0,strlen(arg));
1697 + if (!arg || *arg == '\0')
1698 + fatal("%s line %d: missing groupname",filename, linenum);
1699 + arg[strlen(arg)] = '\0';
1700 + options->lpk.sgroup = xstrdup(arg);
1701 + if (options->lpk.sgroup)
1702 + options->lpk.fgroup = ldap_parse_groups(options->lpk.sgroup);
1703 + memset(arg,0,strlen(arg));
1707 + if (!arg || *arg == '\0')
1708 + fatal("%s line %d: missing filter",filename, linenum);
1709 + arg[strlen(arg)] = '\0';
1710 + options->lpk.filter = xstrdup(arg);
1711 + memset(arg,0,strlen(arg));
1714 + intptr = &options->lpk.tls;
1715 + arg = strdelim(&cp);
1716 + if (!arg || *arg == '\0')
1717 + fatal("%s line %d: missing yes/no argument.",
1718 + filename, linenum);
1719 + value = 0; /* silence compiler */
1720 + if (strcmp(arg, "yes") == 0)
1722 + else if (strcmp(arg, "no") == 0)
1724 + else if (strcmp(arg, "try") == 0)
1727 + fatal("%s line %d: Bad yes/no argument: %s",
1728 + filename, linenum, arg);
1729 + if (*intptr == -1)
1732 + case sBindTimeout:
1733 + intptr = (int *) &options->lpk.b_timeout.tv_sec;
1735 + case sSearchTimeout:
1736 + intptr = (int *) &options->lpk.s_timeout.tv_sec;
1741 + if (!arg || *arg == '\0')
1742 + fatal("%s line %d: missing LpkLdapConf", filename, linenum);
1743 + arg[strlen(arg)] = '\0';
1744 + options->lpk.l_conf = xstrdup(arg);
1745 + memset(arg, 0, strlen(arg));
1750 fatal("%s line %d: Missing handler for opcode %s (%d)",
1751 diff -Nuar --exclude '*.orig' --exclude '*.rej' openssh-5.1p1.orig/servconf.h openssh-5.1p1+lpk/servconf.h
1752 --- openssh-5.1p1.orig/servconf.h 2008-06-10 06:01:51.000000000 -0700
1753 +++ openssh-5.1p1+lpk/servconf.h 2008-08-23 15:02:47.000000000 -0700
1758 +#ifdef WITH_LDAP_PUBKEY
1759 +#include "ldapauth.h"
1762 #define MAX_PORTS 256 /* Max # ports. */
1764 #define MAX_ALLOW_USERS 256 /* Max # users on allow list. */
1766 int use_pam; /* Enable auth via PAM */
1769 +#ifdef WITH_LDAP_PUBKEY
1773 int num_permitted_opens;
1775 diff -Nuar --exclude '*.orig' --exclude '*.rej' openssh-5.1p1.orig/sshd.c openssh-5.1p1+lpk/sshd.c
1776 --- openssh-5.1p1.orig/sshd.c 2008-07-11 00:36:49.000000000 -0700
1777 +++ openssh-5.1p1+lpk/sshd.c 2008-08-23 15:02:47.000000000 -0700
1778 @@ -127,6 +127,10 @@
1780 #endif /* LIBWRAP */
1782 +#ifdef WITH_LDAP_PUBKEY
1783 +#include "ldapauth.h"
1789 @@ -1484,6 +1488,16 @@
1793 +#ifdef WITH_LDAP_PUBKEY
1794 + /* ldap_options_print(&options.lpk); */
1795 + /* XXX initialize/check ldap connection and set *LD */
1796 + if (options.lpk.on) {
1797 + if (options.lpk.l_conf && (ldap_parse_lconf(&options.lpk) < 0) )
1798 + error("[LDAP] could not parse %s", options.lpk.l_conf);
1799 + if (ldap_connect(&options.lpk) < 0)
1800 + error("[LDAP] could not initialize ldap connection");
1803 debug("sshd version %.100s", SSH_RELEASE);
1805 /* Store privilege separation user for later use if required. */
1806 diff -Nuar --exclude '*.orig' --exclude '*.rej' openssh-5.1p1.orig/sshd_config openssh-5.1p1+lpk/sshd_config
1807 --- openssh-5.1p1.orig/sshd_config 2008-07-02 05:35:43.000000000 -0700
1808 +++ openssh-5.1p1+lpk/sshd_config 2008-08-23 15:02:47.000000000 -0700
1809 @@ -109,6 +109,21 @@
1810 # no default banner path
1813 +# here are the new patched ldap related tokens
1814 +# entries in your LDAP must have posixAccount & ldapPublicKey objectclass
1816 +#LpkLdapConf /etc/ldap.conf
1817 +#LpkServers ldap://10.1.7.1/ ldap://10.1.7.2/
1818 +#LpkUserDN ou=users,dc=phear,dc=org
1819 +#LpkGroupDN ou=groups,dc=phear,dc=org
1820 +#LpkBindDN cn=Manager,dc=phear,dc=org
1822 +#LpkServerGroup mail
1823 +#LpkFilter (hostAccess=master.phear.org)
1825 +#LpkSearchTimelimit 3
1826 +#LpkBindTimelimit 3
1828 # override default of no subsystems
1829 Subsystem sftp /usr/libexec/sftp-server
1831 diff -Nuar --exclude '*.orig' --exclude '*.rej' openssh-5.1p1.orig/sshd_config.5 openssh-5.1p1+lpk/sshd_config.5
1832 --- openssh-5.1p1.orig/sshd_config.5 2008-07-02 05:35:43.000000000 -0700
1833 +++ openssh-5.1p1+lpk/sshd_config.5 2008-08-23 15:02:47.000000000 -0700
1834 @@ -1003,6 +1003,62 @@
1837 .Pa /usr/X11R6/bin/xauth .
1839 +Specifies whether LDAP public key retrieval must be used or not. It allow
1840 +an easy centralisation of public keys within an LDAP directory. The argument must be
1845 +Specifies whether LDAP Public keys should parse the specified ldap.conf file
1846 +instead of sshd_config Tokens. The argument must be a valid path to an ldap.conf
1850 +Specifies LDAP one or more [:space:] separated server's url the following form may be used:
1852 +LpkServers ldaps://127.0.0.1 ldap://127.0.0.2 ldap://127.0.0.3
1854 +Specifies the LDAP user DN.
1856 +LpkUserDN ou=users,dc=phear,dc=org
1858 +Specifies the LDAP groups DN.
1860 +LpkGroupDN ou=groups,dc=phear,dc=org
1862 +Specifies the LDAP bind DN to use if necessary.
1864 +LpkBindDN cn=Manager,dc=phear,dc=org
1866 +Specifies the LDAP bind credential.
1869 +.It Cm LpkServerGroup
1870 +Specifies one or more [:space:] separated group the server is part of.
1872 +LpkServerGroup unix mail prod
1874 +Specifies an additional LDAP filter to use for finding SSH keys
1876 +LpkFilter (hostAccess=master.phear.org)
1878 +Specifies if the LDAP server connection must be tried, forced or not used. The argument must be
1884 +.It Cm LpkSearchTimelimit
1885 +Sepcifies the search time limit before the search is considered over. value is
1888 +LpkSearchTimelimit 3
1889 +.It Cm LpkBindTimelimit
1890 +Sepcifies the bind time limit before the connection is considered dead. value is