]> arthur.barton.de Git - netatalk.git/commitdiff
DHX2 and liggcrypt from HEAD
authordidg <didg>
Tue, 2 Dec 2008 03:11:58 +0000 (03:11 +0000)
committerdidg <didg>
Tue, 2 Dec 2008 03:11:58 +0000 (03:11 +0000)
bin/afppasswd/Makefile.am
configure.in
etc/uams/Makefile.am
etc/uams/uams_dhx2_pam.c [new file with mode: 0644]
etc/uams/uams_dhx2_passwd.c [new file with mode: 0644]
macros/libgcrypt.m4 [new file with mode: 0644]
macros/ssl-check.m4
macros/summary.m4

index b9fad222a1e4d4f76243de6e9203b4d8df8c12f3..8e1a0cb050be6dba8fd360ee4550aa1335d0c68e 100644 (file)
@@ -2,7 +2,7 @@
 
 pkgconfdir = @PKGCONFDIR@
 
-if USE_DHX
+if HAVE_OPENSSL
 bin_PROGRAMS = afppasswd
 else
 bin_PROGRAMS =
@@ -15,6 +15,6 @@ CFLAGS = @CFLAGS@ @SSL_CFLAGS@ -I$(top_srcdir)/sys \
     -D_PATH_AFPDPWFILE=\"$(pkgconfdir)/afppasswd\"
 
 install-exec-hook:
-if USE_DHX
+if HAVE_OPENSSL
        chmod u+s $(DESTDIR)$(bindir)/afppasswd
 endif
\ No newline at end of file
index 05db1af1523b6da143bb1f39913246016a778271..e730fce6f364b6012619dcb6030f2a8888d8f98c 100644 (file)
@@ -1,4 +1,4 @@
-dnl $Id: configure.in,v 1.179.2.3.2.37.2.7 2006-09-07 05:02:20 didg Exp $
+dnl $Id: configure.in,v 1.179.2.3.2.37.2.8 2008-12-02 03:11:58 didg Exp $
 dnl configure.in for netatalk
 
 AC_INIT(etc/afpd/main.c)
@@ -919,7 +919,15 @@ if test x"$this_os" = "xtru64"; then
        sysv_style=tru64
 fi
 
-dnl -- look for openssl
+dnl -- check for libgcrypt, if found enables DHX UAM
+AM_PATH_LIBGCRYPT([1:1.2.3],[neta_cv_compile_dhx2=yes
+                       neta_cv_have_libgcrypt=yes
+                       AC_MSG_NOTICE([Enabling DHX2 UAM])
+                       AC_DEFINE(HAVE_LIBGCRYPT, 1, [Define if the DHX2 modules should be built with libgcrypt])
+                       AC_DEFINE(UAM_DHX2, 1, [Define if the DHX2 UAM modules should be compiled])
+                       ])
+
+dnl -- look for openssl, if found enables DHX UAM and Randnum UAM
 AC_PATH_SSL
 
 dnl -- check for crypt
@@ -931,7 +939,7 @@ AC_MSG_CHECKING([whether the PGP UAM should be build])
 AC_ARG_ENABLE(pgp-uam,
        [  --enable-pgp-uam        enable build of PGP UAM module],[
        if test "$enableval" = "yes"; then 
-               if test "$compile_ssl" = "yes"; then 
+               if test "x$neta_cv_have_openssl" = "xyes"; then 
                        AC_DEFINE(UAM_PGP, 1, [Define if the PGP UAM module should be compiled])
                        compile_pgp=yes
                        AC_MSG_RESULT([yes])
@@ -1005,12 +1013,17 @@ AC_SUBST(OVERWRITE_CONFIG)
 AM_CONDITIONAL(SOLARIS_MODULE, test x$solaris_module = xyes)
 AM_CONDITIONAL(COMPILE_TIMELORD, test x$compile_timelord = xyes)
 AM_CONDITIONAL(COMPILE_A2BOOT, test x$compile_a2boot = xyes)
-AM_CONDITIONAL(USE_DHX, test x$compile_ssl = xyes)
+AM_CONDITIONAL(HAVE_LIBGCRYPT, test x$neta_cv_have_libgcrypt = xyes)
+AM_CONDITIONAL(HAVE_OPENSSL, test x$neta_cv_have_openssl = xyes)
+AM_CONDITIONAL(USE_DHX, test x$neta_cv_compile_dhx = xyes)
+AM_CONDITIONAL(USE_DHX2, test x$neta_cv_compile_dhx2 = xyes)
+AM_CONDITIONAL(USE_RANDNUM, test x$neta_cv_have_openssl = xyes)
 AM_CONDITIONAL(USE_KERBEROS, test x$compile_kerberos = xyes)
 AM_CONDITIONAL(USE_PAM_SO, test x$use_pam_so = xyes)
 AM_CONDITIONAL(USE_PAM, test x$netatalk_cv_install_pam = xyes)
 AM_CONDITIONAL(BUILD_PAM, test x$compile_pam = xyes)
 AM_CONDITIONAL(USE_PGP, test x$compile_pgp = xyes)
+AM_CONDITIONAL(DEFAULT_HOOK, test x$neta_cv_have_libgcrypt != xyes && test x$neta_cv_have_openssl != xyes)
 AM_CONDITIONAL(USE_COBALT, test x$sysv_style = xcobalt)
 AM_CONDITIONAL(USE_NETBSD, test x$sysv_style = xnetbsd)
 AM_CONDITIONAL(USE_REDHAT, test x$sysv_style = xredhat)
index 55cba1af64619987bafe0742654072be9f2ec32d..7db37dbcb46cd748cf78ea1c1fbac5b311cf7787 100644 (file)
@@ -7,20 +7,55 @@ SUBDIRS = uams_krb4
 #
 
 UAMS_GENERIC = uams_guest.la uams_passwd.la
+uams_LINKS = uams_clrtxt.so
+dhx_exec_hook = echo
+dhx2_exec_hook = echo
+
+# these are complex: check if DHX and DHX2 module should be build
 
 if USE_DHX
-UAMS_DHX_GENERIC = uams_randnum.la uams_dhx_passwd.la
+if HAVE_OPENSSL
+uams_LINKS += uams_dhx.so
+UAMS_DHX = uams_dhx_passwd.la
+if BUILD_PAM
+UAMS_DHX_PAM = uams_dhx_pam.la
+endif
+if USE_PAM_SO
+dhx_exec_hook += && $(LN_S) uams_dhx_pam.so uams_dhx.so
+else
+dhx_exec_hook += && $(LN_S) uams_dhx_passwd.so uams_dhx.so
 endif
 
-if USE_PGP
-UAMS_PGP = uams_pgp.la
+endif
 endif
 
+if USE_DHX2
+if HAVE_LIBGCRYPT
+uams_LINKS += uams_dhx2.so
+UAMS_DHX2 = uams_dhx2_passwd.la
+if BUILD_PAM
+UAMS_DHX2_PAM = uams_dhx2_pam.la
+endif
+if USE_PAM_SO
+dhx2_exec_hook += && $(LN_S) uams_dhx2_pam.so uams_dhx2.so
+else
+dhx2_exec_hook += && $(LN_S) uams_dhx2_passwd.so uams_dhx2.so
+endif
+endif
+endif
+
+# these are simple, though some the last three depend on OpenSSL
+
 if BUILD_PAM
 UAMS_PAM = uams_pam.la
-if USE_DHX
-UAMS_DHX_PAM = uams_dhx_pam.la
 endif
+
+if USE_RANDNUM
+UAMS_RANDNUM = uams_randnum.la
+endif
+
+if USE_PGP
+UAMS_PGP = uams_pgp.la
 endif
 
 if USE_GSSAPI
@@ -28,12 +63,11 @@ UAMS_GSSAPI = uams_gss.la
 endif
 
 # links
+
 if USE_PAM_SO
 UAMS_CLRTXT_LINK = uams_pam.so
-UAMS_DHX_LINK    = uams_dhx_pam.so
 else
 UAMS_CLRTXT_LINK = uams_passwd.so
-UAMS_DHX_LINK    = uams_dhx_passwd.so
 endif
 
 #
@@ -47,6 +81,8 @@ uams_pam_la_SOURCES        = uams_pam.c
 uams_pgp_la_SOURCES        = uams_pgp.c
 uams_dhx_passwd_la_SOURCES = uams_dhx_passwd.c
 uams_dhx_pam_la_SOURCES    = uams_dhx_pam.c
+uams_dhx2_passwd_la_SOURCES    = uams_dhx2_passwd.c
+uams_dhx2_pam_la_SOURCES       = uams_dhx2_pam.c
 uams_gss_la_SOURCES   = uams_gss.c
 
 #
@@ -54,7 +90,7 @@ uams_gss_la_SOURCES   = uams_gss.c
 #
 
 # do that on behalf of the brokeness of automake 1.4
-CFLAGS = @CFLAGS@ @SSL_CFLAGS@
+CFLAGS = @CFLAGS@ @SSL_CFLAGS@ @LIBGCRYPT_CFLAGS@
 
 uams_guest_la_CFLAGS      = @CFLAGS@
 uams_randnum_la_CFLAGS    = @CFLAGS@ @SSL_CFLAGS@
@@ -63,6 +99,8 @@ uams_pam_la_CFLAGS        = @CFLAGS@ @PAM_CFLAGS@
 uams_pgp_la_CFLAGS        = @CFLAGS@ @SSL_CFLAGS@
 uams_dhx_passwd_la_CFLAGS = @CFLAGS@ @SSL_CFLAGS@
 uams_dhx_pam_la_CFLAGS    = @CFLAGS@ @SSL_CFLAGS@ @PAM_CFLAGS@
+uams_dhx2_passwd_la_CFLAGS     = @CFLAGS@ @LIBGCRYPT_CFLAGS@
+uams_dhx2_pam_la_CFLAGS                = @CFLAGS@ @LIBGCRYPT_CFLAGS@ @PAM_CFLAGS@
 uams_gss_la_CFLAGS       = @CFLAGS@ @GSSAPI_CFLAGS@
 
 uams_guest_la_LDFLAGS      = -module -avoid-version
@@ -71,7 +109,9 @@ uams_passwd_la_LDFLAGS     = -module -avoid-version @CRYPT_LIBS@
 uams_pam_la_LDFLAGS        = -module -avoid-version @PAM_LIBS@
 uams_pgp_la_LDFLAGS        = -module -avoid-version @SSL_LIBS@
 uams_dhx_passwd_la_LDFLAGS = -module -avoid-version @CRYPT_LIBS@ @SSL_LIBS@
-uams_dhx_pam_la_LDFLAGS    = -module -avoid-version @SSL_LIBS@ @PAM_LIBS@
+uams_dhx_pam_la_LDFLAGS                = -module -avoid-version @CRYPT_LIBS@ @SSL_LIBS@ @PAM_LIBS@
+uams_dhx2_passwd_la_LDFLAGS    = -module -avoid-version @CRYPT_LIBS@ @LIBGCRYPT_LIBS@
+uams_dhx2_pam_la_LDFLAGS       = -module -avoid-version @LIBGCRYPT_LIBS@ @PAM_LIBS@
 uams_gss_la_LDFLAGS       = -module -avoid-version @GSSAPI_LIBS@ 
 
 #
@@ -83,37 +123,28 @@ uams_LTLIBRARIES =         \
        $(UAMS_GENERIC)         \
        $(UAMS_PGP)             \
        $(UAMS_PAM)             \
-       $(UAMS_DHX_GENERIC)     \
+       $(UAMS_RANDNUM)         \
+       $(UAMS_DHX)             \
        $(UAMS_DHX_PAM)         \
+       $(UAMS_DHX2)            \
+       $(UAMS_DHX2_PAM)        \
        $(UAMS_GSSAPI)
 
 #
 # link creation
 #
 
-uams_LINKS =
-
-if USE_DHX
 install-exec-hook:
        (cd $(DESTDIR)$(uamsdir)                                        && \
-               rm -f uams_clrtxt.so uams_dhx.so                        && \
+               rm -f $(uams_LINKS)                                     && \
                $(LN_S) $(UAMS_CLRTXT_LINK) uams_clrtxt.so              && \
-               $(LN_S) $(UAMS_DHX_LINK) uams_dhx.so                       \
+               $(dhx_exec_hook)                                        && \
+               $(dhx2_exec_hook)                                       \
        )
 
-uninstall-hook:
-       (cd $(DESTDIR)$(uamsdir)                                        && \
-               rm -f uams_clrtxt.so uams_dhx.so                           \
-       )
-else
-install-exec-hook:
-       (cd $(DESTDIR)$(uamsdir)                                        && \
-               rm -f uams_clrtxt.so                                    && \
-               $(LN_S) $(UAMS_CLRTXT_LINK) uams_clrtxt.so                 \
-       )
+
 
 uninstall-hook:
        (cd $(DESTDIR)$(uamsdir)                                        && \
-               rm -f uams_clrtxt.so                                       \
+               rm -f $(uams_LINKS)                                     \
        )
-endif
diff --git a/etc/uams/uams_dhx2_pam.c b/etc/uams/uams_dhx2_pam.c
new file mode 100644 (file)
index 0000000..713a3a6
--- /dev/null
@@ -0,0 +1,933 @@
+/*
+ * $Id: uams_dhx2_pam.c,v 1.5.2.2 2008-12-02 03:11:59 didg Exp $
+ *
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * Copyright (c) 1999 Adrian Sun (asun@u.washington.edu)
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#if defined (USE_PAM) && defined (UAM_DHX2)
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <atalk/logger.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+#include <errno.h>
+#ifdef HAVE_SECURITY_PAM_APPL_H
+#include <security/pam_appl.h>
+#endif
+#ifdef HAVE_PAM_PAM_APPL_H
+#include <pam/pam_appl.h>
+#endif
+
+
+#ifdef HAVE_LIBGCRYPT
+#include <gcrypt.h>
+#endif /* HAVE_LIBGCRYPT */
+
+#include <atalk/afp.h>
+#include <atalk/uam.h>
+#include "../afpd/globals.h"
+
+/* Number of bits for p which we generate. Everybode out there uses 512, so we beet them */
+#define PRIMEBITS 1024
+
+/* hash a number to a 16-bit quantity */
+#define dhxhash(a) ((((unsigned long) (a) >> 8) ^ \
+                     (unsigned long) (a)) & 0xffff)
+
+/* Some parameters need be maintained across calls */
+static gcry_mpi_t p, Ra;
+static gcry_mpi_t serverNonce;
+static char *K_MD5hash = NULL;
+static int K_hash_len;
+static u_int16_t ID;
+
+/* The initialization vectors for CAST128 are fixed by Apple. */
+static unsigned char dhx_c2siv[] = { 'L', 'W', 'a', 'l', 'l', 'a', 'c', 'e' };
+static unsigned char dhx_s2civ[] = { 'C', 'J', 'a', 'l', 'b', 'e', 'r', 't' };
+
+/* Static variables used to communicate between the conversation function
+ * and the server_login function */
+static pam_handle_t *pamh = NULL;
+static char *PAM_username;
+static char *PAM_password;
+static struct passwd *dhxpwd;
+
+/*********************************************************
+ * Crypto helper func to generate p and g for use in DH.
+ * libgcrypt doesn't provide one directly.
+ * Algorithm taken from GNUTLS:gnutls_dh_primes.c 
+ *********************************************************/
+
+/**
+ * This function will generate a new pair of prime and generator for use in
+ * the Diffie-Hellman key exchange.
+ * The bits value should be one of 768, 1024, 2048, 3072 or 4096.
+ **/
+
+static int
+dh_params_generate (gcry_mpi_t *ret_p, gcry_mpi_t *ret_g, unsigned int bits) {
+
+    int result, times = 0, qbits;
+
+    gcry_mpi_t g = NULL, prime = NULL;
+    gcry_mpi_t *factors = NULL;
+    gcry_error_t err;
+
+    /* Version check should be the very first call because it
+       makes sure that important subsystems are intialized. */
+    if (!gcry_check_version (GCRYPT_VERSION)) {
+        LOG(log_info, logtype_uams, "PAM DHX2: libgcrypt versions mismatch. Need: %u", GCRYPT_VERSION);
+       result = AFPERR_MISC;
+       goto error;
+    }
+
+    if (bits < 256)
+       qbits = bits / 2;
+    else
+       qbits = (bits / 40) + 105;
+
+    if (qbits & 1) /* better have an even number */
+       qbits++;
+
+    /* find a prime number of size bits. */
+    do {
+       if (times) {
+           gcry_mpi_release (prime);
+           gcry_prime_release_factors (factors);
+       }
+       err = gcry_prime_generate (&prime, bits, qbits, &factors, NULL, NULL,
+                                  GCRY_STRONG_RANDOM, GCRY_PRIME_FLAG_SPECIAL_FACTOR);
+       if (err != 0) {
+           result = AFPERR_MISC;
+           goto error;
+       }
+       err = gcry_prime_check (prime, 0);
+       times++;
+    } while (err != 0 && times < 10);
+    
+    if (err != 0) {
+       result = AFPERR_MISC;
+       goto error;
+    }
+
+    /* generate the group generator. */
+    err = gcry_prime_group_generator (&g, prime, factors, NULL);
+    if (err != 0) {
+       result = AFPERR_MISC;
+       goto error;
+    }
+    
+    gcry_prime_release_factors (factors);
+    factors = NULL;
+    
+    if (ret_g)
+       *ret_g = g;
+    else
+       gcry_mpi_release (g);
+    if (ret_p)
+       *ret_p = prime;
+    else
+       gcry_mpi_release (prime);
+    
+    return 0;
+
+error:
+    gcry_prime_release_factors (factors);
+    gcry_mpi_release (g);
+    gcry_mpi_release (prime);
+
+    return result;
+}
+
+
+/* PAM conversation function
+ * Here we assume (for now, at least) that echo on means login name, and
+ * echo off means password.
+ */
+static int PAM_conv (int num_msg,
+                     const struct pam_message **msg,
+                     struct pam_response **resp,
+                     void *appdata_ptr _U_) {
+    int count = 0;
+    struct pam_response *reply;
+
+#define COPY_STRING(s) (s) ? strdup(s) : NULL
+
+    errno = 0;
+
+    if (num_msg < 1) {
+        /* Log Entry */
+        LOG(log_info, logtype_uams, "PAM DHX2 Conversation Err -- %s",
+            strerror(errno));
+        /* Log Entry */
+        return PAM_CONV_ERR;
+    }
+
+    reply = (struct pam_response *)
+        calloc(num_msg, sizeof(struct pam_response));
+
+    if (!reply) {
+        /* Log Entry */
+        LOG(log_info, logtype_uams, "PAM DHX2: Conversation Err -- %s",
+            strerror(errno));
+        /* Log Entry */
+        return PAM_CONV_ERR;
+    }
+
+    for (count = 0; count < num_msg; count++) {
+        char *string = NULL;
+
+        switch (msg[count]->msg_style) {
+        case PAM_PROMPT_ECHO_ON:
+            if (!(string = COPY_STRING(PAM_username))) {
+                /* Log Entry */
+                LOG(log_info, logtype_uams, "PAM DHX2: username failure -- %s",
+                    strerror(errno));
+                /* Log Entry */
+                goto pam_fail_conv;
+            }
+            break;
+        case PAM_PROMPT_ECHO_OFF:
+            if (!(string = COPY_STRING(PAM_password))) {
+                /* Log Entry */
+                LOG(log_info, logtype_uams, "PAM DHX2: passwd failure: --: %s",
+                    strerror(errno));
+                /* Log Entry */
+                goto pam_fail_conv;
+            }
+            break;
+        case PAM_TEXT_INFO:
+#ifdef PAM_BINARY_PROMPT
+        case PAM_BINARY_PROMPT:
+#endif /* PAM_BINARY_PROMPT */
+            /* ignore it... */
+            break;
+        case PAM_ERROR_MSG:
+        default:
+            LOG(log_info, logtype_uams, "PAM DHX2: Binary_Prompt -- %s", strerror(errno));
+            goto pam_fail_conv;
+        }
+
+        if (string) {
+            reply[count].resp_retcode = 0;
+            reply[count].resp = string;
+            string = NULL;
+        }
+    }
+
+    *resp = reply;
+    LOG(log_info, logtype_uams, "PAM DHX2: PAM Success");
+    return PAM_SUCCESS;
+
+pam_fail_conv:
+    for (count = 0; count < num_msg; count++) {
+        if (!reply[count].resp)
+            continue;
+        switch (msg[count]->msg_style) {
+        case PAM_PROMPT_ECHO_OFF:
+        case PAM_PROMPT_ECHO_ON:
+            free(reply[count].resp);
+            break;
+        }
+    }
+    free(reply);
+    /* Log Entry */
+    LOG(log_info, logtype_uams, "PAM DHX2: Conversation Err -- %s",
+        strerror(errno));
+    /* Log Entry */
+    return PAM_CONV_ERR;
+}
+
+static struct pam_conv PAM_conversation = {
+    &PAM_conv,
+    NULL
+};
+
+
+static int dhx2_setup(void *obj, char *ibuf _U_, int ibuflen _U_,
+                     char *rbuf, int *rbuflen)
+{
+    int ret;
+    size_t nwritten;
+    gcry_mpi_t g, Ma;
+    char *Ra_binary = NULL;
+
+    *rbuflen = 0;
+
+    p = gcry_mpi_new(0);
+    g = gcry_mpi_new(0);
+    Ra = gcry_mpi_new(0);
+    Ma = gcry_mpi_new(0);
+
+    /* Generate p and g for DH */
+    ret = dh_params_generate( &p, &g, PRIMEBITS);
+    if (ret != 0) {
+       LOG(log_info, logtype_uams, "DHX2: Couldn't generate p and g");
+       ret = AFPERR_MISC;
+       goto error;
+    }
+
+    /* Generate our random number Ra. */
+    Ra_binary = calloc(1, PRIMEBITS/8);
+    if (Ra_binary == NULL) {
+       ret = AFPERR_MISC;
+       goto error;
+    }
+    gcry_randomize(Ra_binary, PRIMEBITS/8, GCRY_STRONG_RANDOM);
+    gcry_mpi_scan(&Ra, GCRYMPI_FMT_USG, Ra_binary, PRIMEBITS/8, NULL);
+    free(Ra_binary);
+    Ra_binary = NULL;
+
+    /* Ma = g^Ra mod p. This is our "public" key */
+    gcry_mpi_powm(Ma, g, Ra, p);
+
+    /* ------- DH Init done ------ */
+    /* Start building reply packet */
+
+    /* Session ID first */
+    ID = dhxhash(obj);
+    *(u_int16_t *)rbuf = htons(ID);
+    rbuf += 2;
+    *rbuflen += 2;
+
+    /* g is next */
+    gcry_mpi_print( GCRYMPI_FMT_USG, (unsigned char *)rbuf, 4, &nwritten, g);
+    if (nwritten < 4) {
+       memmove( rbuf+4-nwritten, rbuf, nwritten);
+       memset( rbuf, 0, 4-nwritten);
+    }
+    rbuf += 4;
+    *rbuflen += 4;
+
+    /* len = length of p = PRIMEBITS/8 */
+    *(u_int16_t *)rbuf = htons((u_int16_t) PRIMEBITS/8);
+    rbuf += 2;
+    *rbuflen += 2;
+
+    /* p */
+    gcry_mpi_print( GCRYMPI_FMT_USG, (unsigned char *)rbuf, PRIMEBITS/8, NULL, p);
+    rbuf += PRIMEBITS/8;
+    *rbuflen += PRIMEBITS/8;
+
+    /* Ma */
+    gcry_mpi_print( GCRYMPI_FMT_USG, (unsigned char *)rbuf, PRIMEBITS/8, &nwritten, Ma);
+    if (nwritten < PRIMEBITS/8) {
+       memmove(rbuf + (PRIMEBITS/8) - nwritten, rbuf, nwritten);
+       memset(rbuf, 0, (PRIMEBITS/8) - nwritten);
+    }
+    rbuf += PRIMEBITS/8;
+    *rbuflen += PRIMEBITS/8;
+
+    ret = AFPERR_AUTHCONT;
+
+error:                         /* We exit here anyway */
+    /* We will only need p and Ra later, but mustn't forget to release it ! */
+    gcry_mpi_release(g);
+    gcry_mpi_release(Ma);
+    return ret;
+}
+
+/* -------------------------------- */
+static int login(void *obj, char *username, int ulen,  struct passwd **uam_pwd _U_,
+                 char *ibuf, int ibuflen,
+                 char *rbuf, int *rbuflen)
+{
+    if (( dhxpwd = uam_getname(obj, username, ulen)) == NULL ) {
+        LOG(log_info, logtype_uams, "DHX2: unknown username");
+        return AFPERR_PARAM;
+    }
+
+    PAM_username = username;
+    LOG(log_info, logtype_uams, "DHX2 login: %s", username);
+    return dhx2_setup(obj, ibuf, ibuflen, rbuf, rbuflen);
+}
+
+/* -------------------------------- */
+/* dhx login: things are done in a slightly bizarre order to avoid
+ * having to clean things up if there's an error. */
+static int pam_login(void *obj, struct passwd **uam_pwd,
+                     char *ibuf, int ibuflen,
+                     char *rbuf, int *rbuflen)
+{
+    char *username;
+    int len, ulen;
+
+    *rbuflen = 0;
+
+    /* grab some of the options */
+    if (uam_afpserver_option(obj, UAM_OPTION_USERNAME, (void *) &username, &ulen) < 0) {
+        LOG(log_info, logtype_uams, "DHX2: uam_afpserver_option didn't meet uam_option_username  -- %s",
+            strerror(errno));
+        return AFPERR_PARAM;
+    }
+
+    len = (unsigned char) *ibuf++;
+    if ( len > ulen ) {
+        LOG(log_info, logtype_uams, "DHX2: Signature Retieval Failure -- %s",
+            strerror(errno));
+        return AFPERR_PARAM;
+    }
+
+    memcpy(username, ibuf, len );
+    ibuf += len;
+    username[ len ] = '\0';
+
+    if ((unsigned long) ibuf & 1) /* pad to even boundary */
+        ++ibuf;
+
+    return (login(obj, username, ulen, uam_pwd, ibuf, ibuflen, rbuf, rbuflen));
+}
+
+/* ----------------------------- */
+static int pam_login_ext(void *obj, char *uname, struct passwd **uam_pwd,
+                         char *ibuf, int ibuflen,
+                         char *rbuf, int *rbuflen)
+{
+    char *username;
+    int len, ulen;
+    u_int16_t  temp16;
+
+    *rbuflen = 0;
+
+    /* grab some of the options */
+    if (uam_afpserver_option(obj, UAM_OPTION_USERNAME, (void *) &username, &ulen) < 0) {
+        LOG(log_info, logtype_uams, "DHX2: uam_afpserver_option didn't meet uam_option_username  -- %s",
+            strerror(errno));
+        return AFPERR_PARAM;
+    }
+
+    if (*uname != 3)
+        return AFPERR_PARAM;
+    uname++;
+    memcpy(&temp16, uname, sizeof(temp16));
+    len = ntohs(temp16);
+
+    if ( !len || len > ulen ) {
+        LOG(log_info, logtype_uams, "DHX2: Signature Retrieval Failure -- %s",
+            strerror(errno));
+        return AFPERR_PARAM;
+    }
+    memcpy(username, uname +2, len );
+    username[ len ] = '\0';
+
+    return (login(obj, username, ulen, uam_pwd, ibuf, ibuflen, rbuf, rbuflen));
+}
+
+/* -------------------------------- */
+
+static int logincont1(void *obj _U_, char *ibuf, int ibuflen, char *rbuf, int *rbuflen)
+{
+    int ret;
+    size_t nwritten;
+    gcry_mpi_t Mb, K, clientNonce;
+    unsigned char *K_bin = NULL;
+    char serverNonce_bin[16];
+    gcry_cipher_hd_t ctx;
+    gcry_error_t ctxerror;
+
+    *rbuflen = 0;
+
+    Mb = gcry_mpi_new(0);
+    K = gcry_mpi_new(0);
+    clientNonce = gcry_mpi_new(0);
+    serverNonce = gcry_mpi_new(0);
+
+    /* Packet size should be: Session ID + Ma + Encrypted client nonce */
+    if (ibuflen != 2 + PRIMEBITS/8 + 16) {
+       LOG(log_error, logtype_uams, "DHX2: Paket length not correct");
+        ret = AFPERR_PARAM;
+       goto error_noctx;
+    }
+
+    /* Skip session id */
+    ibuf += 2;
+
+    /* Extract Mb, client's "public" key */
+    gcry_mpi_scan(&Mb, GCRYMPI_FMT_USG, ibuf, PRIMEBITS/8, NULL);
+    ibuf += PRIMEBITS/8;
+
+    /* Now finally generate the Key: K = Mb^Ra mod p */
+    gcry_mpi_powm(K, Mb, Ra, p);
+
+    /* We need K in binary form in order to ... */
+    K_bin = calloc(1, PRIMEBITS/8);
+    if (K_bin == NULL) {
+       ret = AFPERR_MISC;
+       goto error_noctx;
+    }
+    gcry_mpi_print(GCRYMPI_FMT_USG, K_bin, PRIMEBITS/8, &nwritten, K);
+    if (nwritten < PRIMEBITS/8) {
+       memmove(K_bin + PRIMEBITS/8 - nwritten, K_bin, nwritten);
+       memset(K_bin, 0, PRIMEBITS/8 - nwritten);
+    }
+
+    /* ... generate the MD5 hash of K. K_MD5hash is what we actually use ! */
+    K_MD5hash = calloc(1, K_hash_len = gcry_md_get_algo_dlen(GCRY_MD_MD5));
+    if (K_MD5hash == NULL) {
+       ret = AFPERR_MISC;
+       goto error_noctx;
+    }
+    gcry_md_hash_buffer(GCRY_MD_MD5, K_MD5hash, K_bin, PRIMEBITS/8);
+    free(K_bin);
+    K_bin = NULL;
+
+    /* FIXME: To support the Reconnect UAM, we need to store this key somewhere */
+
+    /* Set up our encryption context. */
+    ctxerror = gcry_cipher_open( &ctx, GCRY_CIPHER_CAST5, GCRY_CIPHER_MODE_CBC, 0);
+    if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) {
+       ret = AFPERR_MISC;
+        goto error_ctx;
+    }
+    /* Set key */
+    ctxerror = gcry_cipher_setkey(ctx, K_MD5hash, K_hash_len);
+    if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) {
+       ret = AFPERR_MISC;
+        goto error_ctx;
+    }
+    /* Set the initialization vector for client->server transfer. */
+    ctxerror = gcry_cipher_setiv(ctx, dhx_c2siv, sizeof(dhx_c2siv));
+    if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) {
+       ret = AFPERR_MISC;
+        goto error_ctx;
+    }
+    /* Finally: decrypt client's md5_K(client nonce, C2SIV) inplace */
+    ctxerror = gcry_cipher_decrypt(ctx, ibuf, 16, NULL, 0);
+    if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) {
+       ret = AFPERR_MISC;
+        goto error_ctx;
+    }
+    /* Pull out clients nonce */
+    gcry_mpi_scan(&clientNonce, GCRYMPI_FMT_USG, ibuf, 16, NULL);
+    /* Increment nonce */
+    gcry_mpi_add_ui(clientNonce, clientNonce, 1);
+
+    /* Generate our nonce and remember it for Logincont2 */
+    gcry_create_nonce(serverNonce_bin, 16); /* We'll use this here */
+    gcry_mpi_scan(&serverNonce, GCRYMPI_FMT_USG, serverNonce_bin, 16, NULL); /* For use in Logincont2 */
+
+    /* ---- Start building reply packet ---- */
+
+    /* Session ID + 1 first */
+    *(u_int16_t *)rbuf = htons(ID+1);
+    rbuf += 2;
+    *rbuflen += 2;
+
+    /* Client nonce + 1 */
+    gcry_mpi_print(GCRYMPI_FMT_USG, (unsigned char *)rbuf, PRIMEBITS/8, NULL, clientNonce);
+    /* Server nonce */
+    memcpy(rbuf+16, serverNonce_bin, 16);
+
+    /* Set the initialization vector for server->client transfer. */
+    ctxerror = gcry_cipher_setiv(ctx, dhx_s2civ, sizeof(dhx_s2civ));
+    if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) {
+       ret = AFPERR_MISC;
+        goto error_ctx;
+    }
+    /* Encrypt md5_K(clientNonce+1, serverNonce) inplace */
+    ctxerror = gcry_cipher_encrypt(ctx, rbuf, 32, NULL, 0);
+    if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) {
+       ret = AFPERR_MISC;
+       goto error_ctx;
+    }
+    rbuf += 32;
+    *rbuflen += 32;
+
+    ret = AFPERR_AUTHCONT;
+    goto exit;
+
+error_ctx:
+    gcry_cipher_close(ctx);
+error_noctx:
+    gcry_mpi_release(serverNonce);
+    free(K_MD5hash);
+    K_MD5hash=NULL;
+exit:
+    gcry_mpi_release(K);
+    gcry_mpi_release(Mb);
+    gcry_mpi_release(Ra);
+    gcry_mpi_release(p);
+    gcry_mpi_release(clientNonce);
+    return ret;
+}
+
+static int logincont2(void *obj, struct passwd **uam_pwd,
+                     char *ibuf, int ibuflen,
+                     char *rbuf _U_, int *rbuflen)
+{
+    int ret;
+    int PAM_error;
+    char *hostname = NULL;
+    gcry_mpi_t retServerNonce;
+    gcry_cipher_hd_t ctx;
+    gcry_error_t ctxerror;
+
+    *rbuflen = 0;
+
+    /* Packet size should be: Session ID + ServerNonce + Passwd buffer */
+    if (ibuflen != 2 + 16 + 256) {
+        LOG(log_error, logtype_uams, "DHX2: Paket length not correct");
+        ret = AFPERR_PARAM;
+        goto error_noctx;
+    }
+
+    retServerNonce = gcry_mpi_new(0);
+
+    /* For PAM */
+    uam_afpserver_option(obj, UAM_OPTION_CLIENTNAME, (void *) &hostname, NULL);
+
+    /* Set up our encryption context. */
+    ctxerror = gcry_cipher_open( &ctx, GCRY_CIPHER_CAST5, GCRY_CIPHER_MODE_CBC, 0);
+    if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) {
+        ret = AFPERR_MISC;
+        goto error_ctx;
+    }
+    /* Set key */
+    ctxerror = gcry_cipher_setkey(ctx, K_MD5hash, K_hash_len);
+    if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) {
+        ret = AFPERR_MISC;
+        goto error_ctx;
+    }
+    /* Set the initialization vector for client->server transfer. */
+    ctxerror = gcry_cipher_setiv(ctx, dhx_c2siv, sizeof(dhx_c2siv));
+    if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) {
+        ret = AFPERR_MISC;
+        goto error_ctx;
+    }
+
+    /* Skip Session ID */
+    ibuf += 2;
+
+    /* Finally: decrypt client's md5_K(serverNonce+1, passwor, C2SIV) inplace */
+    ctxerror = gcry_cipher_decrypt(ctx, ibuf, 16+256, NULL, 0);
+    if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) {
+        ret = AFPERR_MISC;
+        goto error_ctx;
+    }
+    /* Pull out nonce. Should be serverNonce+1 */
+    gcry_mpi_scan(&retServerNonce, GCRYMPI_FMT_USG, ibuf, 16, NULL);
+    gcry_mpi_sub_ui(retServerNonce, retServerNonce, 1);
+    if ( gcry_mpi_cmp( serverNonce, retServerNonce) != 0) {
+       /* We're hacked!  */
+        ret = AFPERR_NOTAUTH;
+        goto error_ctx;
+    }
+    ibuf += 16;
+
+    LOG(log_info, logtype_uams, "DHX2: logincont2 alive!");
+
+    /* ---- Start authentication with PAM --- */
+
+    /* Set these things up for the conv function */
+    PAM_password = ibuf;
+
+    ret = AFPERR_NOTAUTH;
+    PAM_error = pam_start("netatalk", PAM_username, &PAM_conversation, &pamh);
+    if (PAM_error != PAM_SUCCESS) {
+       LOG(log_info, logtype_uams, "DHX2: PAM_Error: %s",
+           pam_strerror(pamh,PAM_error));
+       goto error_ctx;
+    }
+
+    /* solaris craps out if PAM_TTY and PAM_RHOST aren't set. */
+    pam_set_item(pamh, PAM_TTY, "afpd");
+    pam_set_item(pamh, PAM_RHOST, hostname);
+    PAM_error = pam_authenticate(pamh, 0);
+    if (PAM_error != PAM_SUCCESS) {
+       if (PAM_error == PAM_MAXTRIES)
+           ret = AFPERR_PWDEXPR;
+       LOG(log_info, logtype_uams, "DHX2: PAM_Error: %s",
+           pam_strerror(pamh, PAM_error));
+       goto error_ctx;
+    }
+
+    PAM_error = pam_acct_mgmt(pamh, 0);
+    if (PAM_error != PAM_SUCCESS ) {
+       LOG(log_info, logtype_uams, "DHX2: PAM_Error: %s",
+           pam_strerror(pamh, PAM_error));
+       if (PAM_error == PAM_NEW_AUTHTOK_REQD)    /* password expired */
+           ret = AFPERR_PWDEXPR;
+#ifdef PAM_AUTHTOKEN_REQD
+       else if (PAM_error == PAM_AUTHTOKEN_REQD)
+           ret = AFPERR_PWDCHNG;
+#endif
+       else
+           goto error_ctx;
+    }
+
+#ifndef PAM_CRED_ESTABLISH
+#define PAM_CRED_ESTABLISH PAM_ESTABLISH_CRED
+#endif
+    PAM_error = pam_setcred(pamh, PAM_CRED_ESTABLISH);
+    if (PAM_error != PAM_SUCCESS) {
+       LOG(log_info, logtype_uams, "DHX2: PAM_Error: %s",
+           pam_strerror(pamh, PAM_error));
+       goto error_ctx;
+    }
+
+    PAM_error = pam_open_session(pamh, 0);
+    if (PAM_error != PAM_SUCCESS) {
+       LOG(log_info, logtype_uams, "DHX2: PAM_Error: %s",
+           pam_strerror(pamh, PAM_error));
+       goto error_ctx;
+    }
+
+    memset(ibuf, 0, 256); /* zero out the password */
+    *uam_pwd = dhxpwd;
+    LOG(log_info, logtype_uams, "DHX2: PAM Auth OK!");
+    if ( ret == AFPERR_PWDEXPR)
+        return ret;
+    ret = AFP_OK;
+
+error_ctx:
+    gcry_cipher_close(ctx);
+error_noctx:
+    free(K_MD5hash);
+    K_MD5hash=NULL;
+    gcry_mpi_release(serverNonce);
+    gcry_mpi_release(retServerNonce);    
+    return ret;
+}
+
+static int pam_logincont(void *obj, struct passwd **uam_pwd,
+                         char *ibuf, int ibuflen,
+                         char *rbuf, int *rbuflen)
+{
+    u_int16_t retID;
+    int ret;
+
+    /* check for session id */
+    retID = ntohs(*(u_int16_t *)ibuf);
+    if (retID == ID)
+        ret = logincont1(obj, ibuf, ibuflen, rbuf, rbuflen);
+    else if (retID == ID+1)
+        ret = logincont2(obj, uam_pwd, ibuf,ibuflen, rbuf, rbuflen);
+    else {
+        LOG(log_info, logtype_uams, "DHX2: Session ID Mismatch");
+        ret = AFPERR_PARAM;
+    }
+    return ret;
+}
+
+
+/* logout */
+static void pam_logout() {
+    pam_close_session(pamh, 0);
+    pam_end(pamh, 0);
+    pamh = NULL;
+}
+
+/****************************
+ * --- Change pwd stuff --- */
+
+static int changepw_1(void *obj, char *uname,
+                     char *ibuf, int ibuflen, char *rbuf, int *rbuflen)
+{
+    *rbuflen = 0;
+
+    /* Remember it now, use it in changepw_3 */
+    PAM_username = uname;
+    return( dhx2_setup(obj, ibuf, ibuflen, rbuf, rbuflen) );
+}
+
+static int changepw_2(void *obj, 
+                     char *ibuf, int ibuflen, char *rbuf, int *rbuflen)
+{
+    return( logincont1(obj, ibuf, ibuflen, rbuf, rbuflen) );
+}
+
+static int changepw_3(void *obj _U_,
+                     char *ibuf, int ibuflen _U_, 
+                     char *rbuf _U_, int *rbuflen _U_)
+{
+    int ret;
+    int PAM_error;
+    uid_t uid;
+    pam_handle_t *lpamh;
+    char *hostname = NULL;
+    gcry_mpi_t retServerNonce;
+    gcry_cipher_hd_t ctx;
+    gcry_error_t ctxerror;
+
+    *rbuflen = 0;
+
+    LOG(log_error, logtype_uams, "DHX2 ChangePW: packet 3 processing");
+
+    /* Packet size should be: Session ID + ServerNonce + 2*Passwd buffer */
+    if (ibuflen != 2 + 16 + 2*256) {
+        LOG(log_error, logtype_uams, "DHX2: Paket length not correct");
+        ret = AFPERR_PARAM;
+        goto error_noctx;
+    }
+
+    retServerNonce = gcry_mpi_new(0);
+
+    /* For PAM */
+    uam_afpserver_option(obj, UAM_OPTION_CLIENTNAME, (void *) &hostname, NULL);
+
+    /* Set up our encryption context. */
+    ctxerror = gcry_cipher_open( &ctx, GCRY_CIPHER_CAST5, GCRY_CIPHER_MODE_CBC, 0);
+    if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) {
+        ret = AFPERR_MISC;
+        goto error_ctx;
+    }
+    /* Set key */
+    ctxerror = gcry_cipher_setkey(ctx, K_MD5hash, K_hash_len);
+    if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) {
+        ret = AFPERR_MISC;
+        goto error_ctx;
+    }
+
+    /* Set the initialization vector for client->server transfer. */
+    ctxerror = gcry_cipher_setiv(ctx, dhx_c2siv, sizeof(dhx_c2siv));
+    if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) {
+        ret = AFPERR_MISC;
+        goto error_ctx;
+    }
+
+    /* Skip Session ID */
+    ibuf += 2;
+
+    /* Finally: decrypt client's md5_K(serverNonce+1, 2*password, C2SIV) inplace */
+    ctxerror = gcry_cipher_decrypt(ctx, ibuf, 16+2*256, NULL, 0);
+    if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) {
+        ret = AFPERR_MISC;
+        goto error_ctx;
+    }
+    /* Pull out nonce. Should be serverNonce+1 */
+    gcry_mpi_scan(&retServerNonce, GCRYMPI_FMT_USG, ibuf, 16, NULL);
+    gcry_mpi_sub_ui(retServerNonce, retServerNonce, 1);
+    if ( gcry_mpi_cmp( serverNonce, retServerNonce) != 0) {
+        /* We're hacked!  */
+        ret = AFPERR_NOTAUTH;
+        goto error_ctx;
+    }
+    ibuf += 16;
+
+    /* ---- Start pwd changing with PAM --- */
+    ibuf[255] = '\0';          /* For safety */
+    ibuf[511] = '\0';
+
+    /* check if new and old password are equal */
+    if (memcmp(ibuf, ibuf + 256, 255) == 0) {
+       LOG(log_info, logtype_uams, "DHX2 Chgpwd: new and old password are equal");
+        ret = AFPERR_PWDSAME;
+       goto error_ctx;
+    }
+
+    /* Set these things up for the conv function. PAM_username was set in changepw_1 */
+    PAM_password = ibuf + 256;
+    PAM_error = pam_start("netatalk", PAM_username, &PAM_conversation, &lpamh);
+    if (PAM_error != PAM_SUCCESS) {
+        LOG(log_info, logtype_uams, "DHX2 Chgpwd: PAM error in pam_start");
+       ret = AFPERR_PARAM;
+        goto error_ctx;
+    }
+    pam_set_item(lpamh, PAM_TTY, "afpd");
+    uam_afpserver_option(obj, UAM_OPTION_CLIENTNAME, (void *) &hostname, NULL);
+    pam_set_item(lpamh, PAM_RHOST, hostname);
+    uid = geteuid();
+    seteuid(0);
+    PAM_error = pam_authenticate(lpamh,0);
+    if (PAM_error != PAM_SUCCESS) {
+       LOG(log_info, logtype_uams, "DHX2 Chgpwd: error authenticating with PAM");
+       seteuid(uid);
+       pam_end(lpamh, PAM_error);
+       ret = AFPERR_NOTAUTH;
+       goto error_ctx;
+    }
+    PAM_password = ibuf;
+    PAM_error = pam_chauthtok(lpamh, 0);
+    seteuid(uid); /* un-root ourselves. */
+    memset(ibuf, 0, 512);
+    if (PAM_error != PAM_SUCCESS) {
+       LOG(log_info, logtype_uams, "DHX2 Chgpwd: error changing pw with PAM");
+       pam_end(lpamh, PAM_error);
+       ret = AFPERR_ACCESS;
+       goto error_ctx;
+    }
+    pam_end(lpamh, 0);
+    ret = AFP_OK;
+
+error_ctx:
+    gcry_cipher_close(ctx);
+error_noctx:
+    free(K_MD5hash);
+    K_MD5hash=NULL;
+    gcry_mpi_release(serverNonce);
+    gcry_mpi_release(retServerNonce);
+    return ret;
+}
+
+static int dhx2_changepw(void *obj _U_, char *uname,
+                        struct passwd *pwd _U_, char *ibuf, int ibuflen _U_,
+                        char *rbuf _U_, int *rbuflen _U_)
+{
+    /* We use this to serialize the three incoming FPChangePassword calls */
+    static int dhx2_changepw_status = 1;
+
+    int ret = AFPERR_NOTAUTH;  /* gcc can't figure out it's always initialized */
+
+    switch (dhx2_changepw_status) {
+    case 1:
+       ret = changepw_1( obj, uname, ibuf, ibuflen, rbuf, rbuflen);
+       if ( ret == AFPERR_AUTHCONT)
+           dhx2_changepw_status = 2;
+       break;
+    case 2:
+       ret = changepw_2( obj, ibuf, ibuflen, rbuf, rbuflen);
+        if ( ret == AFPERR_AUTHCONT)
+            dhx2_changepw_status = 3;
+       else
+           dhx2_changepw_status = 1;
+       break;
+    case 3:
+       ret = changepw_3( obj, ibuf, ibuflen, rbuf, rbuflen);
+       dhx2_changepw_status = 1; /* Whether is was succesfull or not: we
+                                    restart anyway !*/
+       break;
+    }
+    return ret;
+}
+
+static int uam_setup(const char *path)
+{
+    if (uam_register(UAM_SERVER_LOGIN_EXT, path, "DHX2", pam_login,
+                     pam_logincont, pam_logout, pam_login_ext) < 0)
+        return -1;
+    if (uam_register(UAM_SERVER_CHANGEPW, path, "DHX2", dhx2_changepw) < 0)
+        return -1;
+    return 0;
+}
+
+static void uam_cleanup(void)
+{
+    uam_unregister(UAM_SERVER_LOGIN, "DHX2");
+    uam_unregister(UAM_SERVER_CHANGEPW, "DHX2");
+}
+
+
+UAM_MODULE_EXPORT struct uam_export uams_dhx2 = {
+    UAM_MODULE_SERVER,
+    UAM_MODULE_VERSION,
+    uam_setup, uam_cleanup
+};
+
+
+UAM_MODULE_EXPORT struct uam_export uams_dhx2_pam = {
+    UAM_MODULE_SERVER,
+    UAM_MODULE_VERSION,
+    uam_setup, uam_cleanup
+};
+
+#endif /* USE_PAM && UAM_DHX2 */
+
diff --git a/etc/uams/uams_dhx2_passwd.c b/etc/uams/uams_dhx2_passwd.c
new file mode 100644 (file)
index 0000000..c5b09fe
--- /dev/null
@@ -0,0 +1,639 @@
+/*
+ * $Id: uams_dhx2_passwd.c,v 1.4.2.2 2008-12-02 03:11:59 didg Exp $
+ *
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * Copyright (c) 1999 Adrian Sun (asun@u.washington.edu)
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#ifdef UAM_DHX2
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <pwd.h>
+
+#ifdef NETBSD
+#define _XOPEN_SOURCE 500 /* for crypt() */
+#endif
+#ifdef FREEBSD
+#define _XOPEN_SOURCE /* for crypt() */
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_CRYPT_H
+#include <crypt.h>
+#endif
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#ifdef HAVE_TIME_H
+#include <time.h>
+#endif
+
+#ifdef SHADOWPW
+#include <shadow.h>
+#endif
+
+#ifdef HAVE_LIBGCRYPT
+#include <gcrypt.h>
+#endif
+
+#include <atalk/afp.h>
+#include <atalk/uam.h>
+#include <atalk/logger.h>
+
+/* Number of bits for p which we generate. Everybode out there uses 512, so we beet them */
+#define PRIMEBITS 1024
+
+/* hash a number to a 16-bit quantity */
+#define dhxhash(a) ((((unsigned long) (a) >> 8) ^ \
+                     (unsigned long) (a)) & 0xffff)
+
+/* Some parameters need be maintained across calls */
+static gcry_mpi_t p, Ra;
+static gcry_mpi_t serverNonce;
+static char *K_MD5hash = NULL;
+static int K_hash_len;
+static u_int16_t ID;
+
+/* The initialization vectors for CAST128 are fixed by Apple. */
+static unsigned char dhx_c2siv[] = { 'L', 'W', 'a', 'l', 'l', 'a', 'c', 'e' };
+static unsigned char dhx_s2civ[] = { 'C', 'J', 'a', 'l', 'b', 'e', 'r', 't' };
+
+/* Static variables used to communicate between the conversation function
+ * and the server_login function */
+static struct passwd *dhxpwd;
+
+/*********************************************************
+ * Crypto helper func to generate p and g for use in DH.
+ * libgcrypt doesn't provide one directly.
+ * Algorithm taken from GNUTLS:gnutls_dh_primes.c 
+ *********************************************************/
+
+/**
+ * This function will generate a new pair of prime and generator for use in
+ * the Diffie-Hellman key exchange.
+ * The bits value should be one of 768, 1024, 2048, 3072 or 4096.
+ **/
+
+static int
+dh_params_generate (gcry_mpi_t *ret_p, gcry_mpi_t *ret_g, unsigned int bits) {
+
+    int result, times = 0, qbits;
+
+    gcry_mpi_t g = NULL, prime = NULL;
+    gcry_mpi_t *factors = NULL;
+    gcry_error_t err;
+
+    /* Version check should be the very first call because it
+       makes sure that important subsystems are intialized. */
+    if (!gcry_check_version (GCRYPT_VERSION)) {
+        LOG(log_info, logtype_uams, "PAM DHX2: libgcrypt versions mismatch. Need: %u", GCRYPT_VERSION);
+       result = AFPERR_MISC;
+       goto error;
+    }
+
+    if (bits < 256)
+       qbits = bits / 2;
+    else
+       qbits = (bits / 40) + 105;
+
+    if (qbits & 1) /* better have an even number */
+       qbits++;
+
+    /* find a prime number of size bits. */
+    do {
+       if (times) {
+           gcry_mpi_release (prime);
+           gcry_prime_release_factors (factors);
+       }
+       err = gcry_prime_generate (&prime, bits, qbits, &factors, NULL, NULL,
+                                  GCRY_STRONG_RANDOM, GCRY_PRIME_FLAG_SPECIAL_FACTOR);
+       if (err != 0) {
+           result = AFPERR_MISC;
+           goto error;
+       }
+       err = gcry_prime_check (prime, 0);
+       times++;
+    } while (err != 0 && times < 10);
+    
+    if (err != 0) {
+       result = AFPERR_MISC;
+       goto error;
+    }
+
+    /* generate the group generator. */
+    err = gcry_prime_group_generator (&g, prime, factors, NULL);
+    if (err != 0) {
+       result = AFPERR_MISC;
+       goto error;
+    }
+    
+    gcry_prime_release_factors (factors);
+    factors = NULL;
+    
+    if (ret_g)
+       *ret_g = g;
+    else
+       gcry_mpi_release (g);
+    if (ret_p)
+       *ret_p = prime;
+    else
+       gcry_mpi_release (prime);
+    
+    return 0;
+
+error:
+    gcry_prime_release_factors (factors);
+    gcry_mpi_release (g);
+    gcry_mpi_release (prime);
+
+    return result;
+}
+
+static int dhx2_setup(void *obj, char *ibuf _U_, int ibuflen _U_,
+                     char *rbuf, int *rbuflen)
+{
+    int ret;
+    size_t nwritten;
+    gcry_mpi_t g, Ma;
+    char *Ra_binary = NULL;
+#ifdef SHADOWPW
+    struct spwd *sp;
+#endif /* SHADOWPW */
+
+    *rbuflen = 0;
+
+    /* Initialize passwd/shadow */
+#ifdef SHADOWPW
+    if (( sp = getspnam( dhxpwd->pw_name )) == NULL ) {
+        LOG(log_info, logtype_uams, "DHX2: no shadow passwd entry for this user");
+        return AFPERR_NOTAUTH;
+    }
+    dhxpwd->pw_passwd = sp->sp_pwdp;
+#endif /* SHADOWPW */
+
+    if (!dhxpwd->pw_passwd)
+       return AFPERR_NOTAUTH;
+
+    /* Initialize DH params */
+
+    p = gcry_mpi_new(0);
+    g = gcry_mpi_new(0);
+    Ra = gcry_mpi_new(0);
+    Ma = gcry_mpi_new(0);
+
+    /* Generate p and g for DH */
+    ret = dh_params_generate( &p, &g, PRIMEBITS);
+    if (ret != 0) {
+       LOG(log_info, logtype_uams, "DHX2: Couldn't generate p and g");
+       ret = AFPERR_MISC;
+       goto error;
+    }
+
+    /* Generate our random number Ra. */
+    Ra_binary = calloc(1, PRIMEBITS/8);
+    if (Ra_binary == NULL) {
+       ret = AFPERR_MISC;
+       goto error;
+    }
+    gcry_randomize(Ra_binary, PRIMEBITS/8, GCRY_STRONG_RANDOM);
+    gcry_mpi_scan(&Ra, GCRYMPI_FMT_USG, Ra_binary, PRIMEBITS/8, NULL);
+    free(Ra_binary);
+    Ra_binary = NULL;
+
+    /* Ma = g^Ra mod p. This is our "public" key */
+    gcry_mpi_powm(Ma, g, Ra, p);
+
+    /* ------- DH Init done ------ */
+    /* Start building reply packet */
+
+    /* Session ID first */
+    ID = dhxhash(obj);
+    *(u_int16_t *)rbuf = htons(ID);
+    rbuf += 2;
+    *rbuflen += 2;
+
+    /* g is next */
+    gcry_mpi_print( GCRYMPI_FMT_USG, (unsigned char *)rbuf, 4, &nwritten, g);
+    if (nwritten < 4) {
+       memmove( rbuf+4-nwritten, rbuf, nwritten);
+       memset( rbuf, 0, 4-nwritten);
+    }
+    rbuf += 4;
+    *rbuflen += 4;
+
+    /* len = length of p = PRIMEBITS/8 */
+    *(u_int16_t *)rbuf = htons((u_int16_t) PRIMEBITS/8);
+    rbuf += 2;
+    *rbuflen += 2;
+
+    /* p */
+    gcry_mpi_print( GCRYMPI_FMT_USG, (unsigned char *)rbuf, PRIMEBITS/8, NULL, p);
+    rbuf += PRIMEBITS/8;
+    *rbuflen += PRIMEBITS/8;
+
+    /* Ma */
+    gcry_mpi_print( GCRYMPI_FMT_USG, (unsigned char *)rbuf, PRIMEBITS/8, &nwritten, Ma);
+    if (nwritten < PRIMEBITS/8) {
+       memmove(rbuf + (PRIMEBITS/8) - nwritten, rbuf, nwritten);
+       memset(rbuf, 0, (PRIMEBITS/8) - nwritten);
+    }
+    rbuf += PRIMEBITS/8;
+    *rbuflen += PRIMEBITS/8;
+
+    ret = AFPERR_AUTHCONT;
+
+error:                         /* We exit here anyway */
+    /* We will only need p and Ra later, but mustn't forget to release it ! */
+    gcry_mpi_release(g);
+    gcry_mpi_release(Ma);
+    return ret;
+}
+
+/* -------------------------------- */
+static int login(void *obj, char *username, int ulen,  struct passwd **uam_pwd _U_,
+                 char *ibuf, int ibuflen,
+                 char *rbuf, int *rbuflen)
+{
+    if (( dhxpwd = uam_getname(obj, username, ulen)) == NULL ) {
+        LOG(log_info, logtype_uams, "DHX2: unknown username");
+        return AFPERR_PARAM;
+    }
+
+    LOG(log_info, logtype_uams, "DHX2 login: %s", username);
+    return dhx2_setup(obj, ibuf, ibuflen, rbuf, rbuflen);
+}
+
+/* -------------------------------- */
+/* dhx login: things are done in a slightly bizarre order to avoid
+ * having to clean things up if there's an error. */
+static int passwd_login(void *obj, struct passwd **uam_pwd,
+                     char *ibuf, int ibuflen,
+                     char *rbuf, int *rbuflen)
+{
+    char *username;
+    int len, ulen;
+
+    *rbuflen = 0;
+
+    /* grab some of the options */
+    if (uam_afpserver_option(obj, UAM_OPTION_USERNAME, (void *) &username, &ulen) < 0) {
+        LOG(log_info, logtype_uams, "DHX2: uam_afpserver_option didn't meet uam_option_username  -- %s",
+            strerror(errno));
+        return AFPERR_PARAM;
+    }
+
+    len = (unsigned char) *ibuf++;
+    if ( len > ulen ) {
+        LOG(log_info, logtype_uams, "DHX2: Signature Retieval Failure -- %s",
+            strerror(errno));
+        return AFPERR_PARAM;
+    }
+
+    memcpy(username, ibuf, len );
+    ibuf += len;
+    username[ len ] = '\0';
+
+    if ((unsigned long) ibuf & 1) /* pad to even boundary */
+        ++ibuf;
+
+    return (login(obj, username, ulen, uam_pwd, ibuf, ibuflen, rbuf, rbuflen));
+}
+
+/* ----------------------------- */
+static int passwd_login_ext(void *obj, char *uname, struct passwd **uam_pwd,
+                         char *ibuf, int ibuflen,
+                         char *rbuf, int *rbuflen)
+{
+    char *username;
+    int len, ulen;
+    u_int16_t  temp16;
+
+    *rbuflen = 0;
+
+    /* grab some of the options */
+    if (uam_afpserver_option(obj, UAM_OPTION_USERNAME, (void *) &username, &ulen) < 0) {
+        LOG(log_info, logtype_uams, "DHX2: uam_afpserver_option didn't meet uam_option_username  -- %s",
+            strerror(errno));
+        return AFPERR_PARAM;
+    }
+
+    if (*uname != 3)
+        return AFPERR_PARAM;
+    uname++;
+    memcpy(&temp16, uname, sizeof(temp16));
+    len = ntohs(temp16);
+
+    if ( !len || len > ulen ) {
+        LOG(log_info, logtype_uams, "DHX2: Signature Retrieval Failure -- %s",
+            strerror(errno));
+        return AFPERR_PARAM;
+    }
+    memcpy(username, uname +2, len );
+    username[ len ] = '\0';
+
+    return (login(obj, username, ulen, uam_pwd, ibuf, ibuflen, rbuf, rbuflen));
+}
+
+/* -------------------------------- */
+
+static int logincont1(void *obj _U_, struct passwd **uam_pwd _U_,
+                         char *ibuf, int ibuflen,
+                         char *rbuf, int *rbuflen)
+{
+    size_t nwritten;
+    int ret;
+    gcry_mpi_t Mb, K, clientNonce;
+    unsigned char *K_bin = NULL;
+    char serverNonce_bin[16];
+    gcry_cipher_hd_t ctx;
+    gcry_error_t ctxerror;
+
+    *rbuflen = 0;
+
+    Mb = gcry_mpi_new(0);
+    K = gcry_mpi_new(0);
+    clientNonce = gcry_mpi_new(0);
+    serverNonce = gcry_mpi_new(0);
+
+    /* Packet size should be: Session ID + Ma + Encrypted client nonce */
+    if (ibuflen != 2 + PRIMEBITS/8 + 16) {
+       LOG(log_error, logtype_uams, "DHX2: Paket length not correct");
+        ret = AFPERR_PARAM;
+       goto error_noctx;
+    }
+
+    /* Skip session id */
+    ibuf += 2;
+
+    /* Extract Mb, client's "public" key */
+    gcry_mpi_scan(&Mb, GCRYMPI_FMT_USG, ibuf, PRIMEBITS/8, NULL);
+    ibuf += PRIMEBITS/8;
+
+    /* Now finally generate the Key: K = Mb^Ra mod p */
+    gcry_mpi_powm(K, Mb, Ra, p);
+
+    /* We need K in binary form in order to ... */
+    K_bin = calloc(1, PRIMEBITS/8);
+    if (K_bin == NULL) {
+       ret = AFPERR_MISC;
+       goto error_noctx;
+    }
+    gcry_mpi_print(GCRYMPI_FMT_USG, K_bin, PRIMEBITS/8, &nwritten, K);
+    if (nwritten < PRIMEBITS/8) {
+       memmove(K_bin + PRIMEBITS/8 - nwritten, K_bin, nwritten);
+       memset(K_bin, 0, PRIMEBITS/8 - nwritten);
+    }
+
+    /* ... generate the MD5 hash of K. K_MD5hash is what we actually use ! */
+    K_MD5hash = calloc(1, K_hash_len = gcry_md_get_algo_dlen(GCRY_MD_MD5));
+    if (K_MD5hash == NULL) {
+       ret = AFPERR_MISC;
+       goto error_noctx;
+    }
+    gcry_md_hash_buffer(GCRY_MD_MD5, K_MD5hash, K_bin, PRIMEBITS/8);
+    free(K_bin); 
+    K_bin = NULL; 
+
+    /* FIXME: To support the Reconnect UAM, we need to store this key somewhere */
+
+    /* Set up our encryption context. */
+    ctxerror = gcry_cipher_open( &ctx, GCRY_CIPHER_CAST5, GCRY_CIPHER_MODE_CBC, 0);
+    if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) {
+       ret = AFPERR_MISC;
+        goto error_ctx;
+    }
+    /* Set key */
+    ctxerror = gcry_cipher_setkey(ctx, K_MD5hash, K_hash_len);
+    if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) {
+       ret = AFPERR_MISC;
+        goto error_ctx;
+    }
+    /* Set the initialization vector for client->server transfer. */
+    ctxerror = gcry_cipher_setiv(ctx, dhx_c2siv, sizeof(dhx_c2siv));
+    if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) {
+       ret = AFPERR_MISC;
+        goto error_ctx;
+    }
+    /* Finally: decrypt client's md5_K(client nonce, C2SIV) inplace */
+    ctxerror = gcry_cipher_decrypt(ctx, ibuf, 16, NULL, 0);
+    if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) {
+       ret = AFPERR_MISC;
+        goto error_ctx;
+    }
+    /* Pull out clients nonce */
+    gcry_mpi_scan(&clientNonce, GCRYMPI_FMT_USG, ibuf, 16, NULL);
+    /* Increment nonce */
+    gcry_mpi_add_ui(clientNonce, clientNonce, 1);
+
+    /* Generate our nonce and remember it for Logincont2 */
+    gcry_create_nonce(serverNonce_bin, 16); /* We'll use this here */
+    gcry_mpi_scan(&serverNonce, GCRYMPI_FMT_USG, serverNonce_bin, 16, NULL); /* For use in Logincont2 */
+
+    /* ---- Start building reply packet ---- */
+
+    /* Session ID + 1 first */
+    *(u_int16_t *)rbuf = htons(ID+1);
+    rbuf += 2;
+    *rbuflen += 2;
+
+    /* Client nonce + 1 */
+    gcry_mpi_print(GCRYMPI_FMT_USG, (unsigned char *)rbuf, PRIMEBITS/8, NULL, clientNonce);
+    /* Server nonce */
+    memcpy(rbuf+16, serverNonce_bin, 16);
+
+    /* Set the initialization vector for server->client transfer. */
+    ctxerror = gcry_cipher_setiv(ctx, dhx_s2civ, sizeof(dhx_s2civ));
+    if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) {
+       ret = AFPERR_MISC;
+        goto error_ctx;
+    }
+    /* Encrypt md5_K(clientNonce+1, serverNonce) inplace */
+    ctxerror = gcry_cipher_encrypt(ctx, rbuf, 32, NULL, 0);
+    if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) {
+       ret = AFPERR_MISC;
+       goto error_ctx;
+    }
+    rbuf += 32;
+    *rbuflen += 32;
+
+    ret = AFPERR_AUTHCONT;
+    goto exit;
+
+error_ctx:
+    gcry_cipher_close(ctx);
+error_noctx:
+    gcry_mpi_release(serverNonce);
+    free(K_MD5hash);
+    K_MD5hash=NULL;
+exit:
+    gcry_mpi_release(K);
+    gcry_mpi_release(Mb);
+    gcry_mpi_release(Ra);
+    gcry_mpi_release(p);
+    gcry_mpi_release(clientNonce);
+    return ret;
+}
+
+static int logincont2(void *obj _U_, struct passwd **uam_pwd,
+                     char *ibuf, int ibuflen,
+                     char *rbuf _U_, int *rbuflen)
+{
+#ifdef SHADOWPW
+    struct spwd *sp;
+#endif /* SHADOWPW */
+    int ret;
+    char *p;
+    gcry_mpi_t retServerNonce;
+    gcry_cipher_hd_t ctx;
+    gcry_error_t ctxerror;
+
+    *rbuflen = 0;
+    /* Packet size should be: Session ID + ServerNonce + Passwd buffer */
+    if (ibuflen != 2 + 16 + 256) {
+        LOG(log_error, logtype_uams, "DHX2: Paket length not correct");
+        ret = AFPERR_PARAM;
+        goto error_noctx;
+    }
+
+    retServerNonce = gcry_mpi_new(0);
+
+    /* Set up our encryption context. */
+    ctxerror = gcry_cipher_open( &ctx, GCRY_CIPHER_CAST5, GCRY_CIPHER_MODE_CBC, 0);
+    if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) {
+        ret = AFPERR_MISC;
+        goto error_ctx;
+    }
+    /* Set key */
+    ctxerror = gcry_cipher_setkey(ctx, K_MD5hash, K_hash_len);
+    if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) {
+        ret = AFPERR_MISC;
+        goto error_ctx;
+    }
+    /* Set the initialization vector for client->server transfer. */
+    ctxerror = gcry_cipher_setiv(ctx, dhx_c2siv, sizeof(dhx_c2siv));
+    if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) {
+        ret = AFPERR_MISC;
+        goto error_ctx;
+    }
+
+    /* Skip Session ID */
+    ibuf += 2;
+
+    /* Finally: decrypt client's md5_K(serverNonce+1, passwor, C2SIV) inplace */
+    ctxerror = gcry_cipher_decrypt(ctx, ibuf, 16+256, NULL, 0);
+    if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) {
+        ret = AFPERR_MISC;
+        goto error_ctx;
+    }
+    /* Pull out nonce. Should be serverNonce+1 */
+    gcry_mpi_scan(&retServerNonce, GCRYMPI_FMT_USG, ibuf, 16, NULL);
+    gcry_mpi_sub_ui(retServerNonce, retServerNonce, 1);
+    if ( gcry_mpi_cmp( serverNonce, retServerNonce) != 0) {
+       /* We're hacked!  */
+        ret = AFPERR_NOTAUTH;
+        goto error_ctx;
+    }
+    ibuf += 16;                        /* ibuf now point to passwd in cleartext */
+
+    /* ---- Start authentication --- */
+    ret = AFPERR_NOTAUTH;
+
+    p = crypt( ibuf, dhxpwd->pw_passwd );
+    memset(ibuf, 0, 255);
+    if ( strcmp( p, dhxpwd->pw_passwd ) == 0 ) {
+       *uam_pwd = dhxpwd;
+       ret = AFP_OK;
+    }
+
+#ifdef SHADOWPW
+    if (( sp = getspnam( dhxpwd->pw_name )) == NULL ) {
+        LOG(log_info, logtype_uams, "no shadow passwd entry for %s", dhxpwd->pw_name);
+        ret = AFPERR_NOTAUTH;
+       goto exit;
+    }
+
+    /* check for expired password */
+    if (sp && sp->sp_max != -1 && sp->sp_lstchg) {
+        time_t now = time(NULL) / (60*60*24);
+        int32_t expire_days = sp->sp_lstchg - now + sp->sp_max;
+        if ( expire_days < 0 ) {
+           LOG(log_info, logtype_uams, "password for user %s expired", dhxpwd->pw_name);
+           ret = AFPERR_PWDEXPR;
+           goto exit;
+        }
+    }
+#endif /* SHADOWPW */
+
+error_ctx:
+    gcry_cipher_close(ctx);
+error_noctx:
+exit:
+    free(K_MD5hash);
+    K_MD5hash=NULL;
+    gcry_mpi_release(serverNonce);
+    gcry_mpi_release(retServerNonce);
+    return ret;
+}
+
+static int passwd_logincont(void *obj, struct passwd **uam_pwd,
+                         char *ibuf, int ibuflen,
+                         char *rbuf, int *rbuflen)
+{
+    u_int16_t retID;
+    int ret;
+
+    /* check for session id */
+    retID = ntohs(*(u_int16_t *)ibuf);
+    if (retID == ID)
+        ret = logincont1(obj, uam_pwd, ibuf, ibuflen, rbuf, rbuflen);
+    else if (retID == ID+1)
+        ret = logincont2(obj, uam_pwd, ibuf,ibuflen, rbuf, rbuflen);
+    else {
+        LOG(log_info, logtype_uams, "DHX2: Session ID Mismatch");
+        ret = AFPERR_PARAM;
+    }
+    return ret;
+}
+
+static int uam_setup(const char *path)
+{
+    if (uam_register(UAM_SERVER_LOGIN_EXT, path, "DHX2", passwd_login,
+                     passwd_logincont, NULL, passwd_login_ext) < 0)
+        return -1;
+    return 0;
+}
+
+static void uam_cleanup(void)
+{
+    uam_unregister(UAM_SERVER_LOGIN, "DHX2");
+}
+
+
+UAM_MODULE_EXPORT struct uam_export uams_dhx2 = {
+    UAM_MODULE_SERVER,
+    UAM_MODULE_VERSION,
+    uam_setup, uam_cleanup
+};
+
+
+UAM_MODULE_EXPORT struct uam_export uams_dhx2_passwd = {
+    UAM_MODULE_SERVER,
+    UAM_MODULE_VERSION,
+    uam_setup, uam_cleanup
+};
+
+#endif /* UAM_DHX2 */
+
diff --git a/macros/libgcrypt.m4 b/macros/libgcrypt.m4
new file mode 100644 (file)
index 0000000..907f251
--- /dev/null
@@ -0,0 +1,101 @@
+dnl AM_PATH_LIBGCRYPT([MINIMUM-VERSION,
+dnl                   [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND ]]])
+dnl Test for libgcrypt and define LIBGCRYPT_CFLAGS and LIBGCRYPT_LIBS.
+dnl MINIMUN-VERSION is a string with the version number optionalliy prefixed
+dnl with the API version to also check the API compatibility. Example:
+dnl a MINIMUN-VERSION of 1:1.2.5 won't pass the test unless the installed 
+dnl version of libgcrypt is at least 1.2.5 *and* the API number is 1.  Using
+dnl this features allows to prevent build against newer versions of libgcrypt
+dnl with a changed API.
+dnl
+AC_DEFUN([AM_PATH_LIBGCRYPT],
+[ AC_ARG_WITH(libgcrypt-dir,
+            AC_HELP_STRING([--with-libgcrypt-dir=PATH],
+                           [path where LIBGCRYPT is installed (optional). 
+                           Must contain lib and include dirs.]),
+     libgcrypt_config_prefix="$withval", libgcrypt_config_prefix="")
+  if test x$libgcrypt_config_prefix != x ; then
+     if test x${LIBGCRYPT_CONFIG+set} != xset ; then
+        LIBGCRYPT_CONFIG=$libgcrypt_config_prefix/bin/libgcrypt-config
+     fi
+  fi
+
+  ok=no
+
+if test x$libgcrypt_config_prefix != xno ; then
+
+  AC_PATH_PROG(LIBGCRYPT_CONFIG, libgcrypt-config, no)
+  tmp=ifelse([$1], ,1:1.2.0,$1)
+  if echo "$tmp" | grep ':' >/dev/null 2>/dev/null ; then
+     req_libgcrypt_api=`echo "$tmp"     | sed 's/\(.*\):\(.*\)/\1/'`
+     min_libgcrypt_version=`echo "$tmp" | sed 's/\(.*\):\(.*\)/\2/'`
+  else
+     req_libgcrypt_api=0
+     min_libgcrypt_version="$tmp"
+  fi
+
+  AC_MSG_CHECKING(for LIBGCRYPT - version >= $min_libgcrypt_version)
+  if test "$LIBGCRYPT_CONFIG" != "no" ; then
+    req_major=`echo $min_libgcrypt_version | \
+               sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\1/'`
+    req_minor=`echo $min_libgcrypt_version | \
+               sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\2/'`
+    req_micro=`echo $min_libgcrypt_version | \
+               sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\3/'`
+    libgcrypt_config_version=`$LIBGCRYPT_CONFIG --version`
+    major=`echo $libgcrypt_config_version | \
+               sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\).*/\1/'`
+    minor=`echo $libgcrypt_config_version | \
+               sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\).*/\2/'`
+    micro=`echo $libgcrypt_config_version | \
+               sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\).*/\3/'`
+    if test "$major" -gt "$req_major"; then
+        ok=yes
+    else 
+        if test "$major" -eq "$req_major"; then
+            if test "$minor" -gt "$req_minor"; then
+               ok=yes
+            else
+               if test "$minor" -eq "$req_minor"; then
+                   if test "$micro" -ge "$req_micro"; then
+                     ok=yes
+                   fi
+               fi
+            fi
+          fi
+       fi
+  fi
+fi
+  if test $ok = yes; then
+    AC_MSG_RESULT([yes ($libgcrypt_config_version)])
+  else
+    AC_MSG_RESULT(no)
+  fi
+  if test $ok = yes; then
+     # If we have a recent libgcrypt, we should also check that the
+     # API is compatible
+     if test "$req_libgcrypt_api" -gt 0 ; then
+        tmp=`$LIBGCRYPT_CONFIG --api-version 2>/dev/null || echo 0`
+        if test "$tmp" -gt 0 ; then
+           AC_MSG_CHECKING([LIBGCRYPT API version])
+           if test "$req_libgcrypt_api" -eq "$tmp" ; then
+             AC_MSG_RESULT([okay])
+           else
+             ok=no
+             AC_MSG_RESULT([does not match. want=$req_libgcrypt_api got=$tmp])
+           fi
+        fi
+     fi
+  fi
+  if test $ok = yes; then
+    LIBGCRYPT_CFLAGS=`$LIBGCRYPT_CONFIG --cflags`
+    LIBGCRYPT_LIBS=`$LIBGCRYPT_CONFIG --libs`
+    ifelse([$2], , :, [$2])
+  else
+    LIBGCRYPT_CFLAGS=""
+    LIBGCRYPT_LIBS=""
+    ifelse([$3], , :, [$3])
+  fi
+  AC_SUBST(LIBGCRYPT_CFLAGS)
+  AC_SUBST(LIBGCRYPT_LIBS)
+])
\ No newline at end of file
index 75365b1a65e69e57af3e462d334a1abe2eeb4cb4..d4c362c9b1bf753e4e49837deacf502fdfab211f 100644 (file)
@@ -1,4 +1,4 @@
-dnl $Id: ssl-check.m4,v 1.8.6.4 2004-08-11 03:03:50 bfernhomberg Exp $
+dnl $Id: ssl-check.m4,v 1.8.6.4.2.1 2008-12-02 03:11:59 didg Exp $
 dnl Autoconf macro to check for SSL or OpenSSL
 
 AC_DEFUN([AC_CRYPT], [
@@ -38,7 +38,8 @@ AC_DEFUN([AC_PATH_SSL], [
        SSL_LIBS=""
        saved_LIBS=$LIBS
        saved_CFLAGS=$CFLAGS
-       compile_ssl=no
+       neta_cv_have_openssl=no
+dnl    compile_ssl=no
 
        dnl make sure atalk_libname is defined beforehand
        [[ -n "$atalk_libname" ]] || AC_MSG_ERROR([internal error, atalk_libname undefined])
@@ -64,13 +65,14 @@ dnl FIXME: The following looks crude and probably doesn't work properly.
 
                                AC_DEFINE(OPENSSL_DHX,  1, [Define if the OpenSSL DHX modules should be built])
                                AC_DEFINE(UAM_DHX,      1, [Define if the DHX UAM modules should be compiled])
-                               compile_ssl=yes
+                               neta_cv_have_openssl=yes
+                               neta_cv_compile_dhx=yes
                                CFLAGS=$saved_CFLAGS
                                LIBS=$saved_LIBS
                                break
                        fi
                done
-               if test "x$compile_ssl" = "xno"; then
+               if test "x$neta_cv_have_openssl" = "xno"; then
                        AC_MSG_RESULT([no])
                fi
        fi
index 7d109df6052666e207cf7c1ee50441c6d3a0e63b..bb0a32a17bc9d205c98380e0affd66e606cc792d 100644 (file)
@@ -1,4 +1,4 @@
-dnl $Id: summary.m4,v 1.1.2.5.2.1 2004-12-07 18:47:28 bfernhomberg Exp $
+dnl $Id: summary.m4,v 1.1.2.5.2.2 2008-12-02 03:11:59 didg Exp $
 dnl Autoconf macros, display configure summary
 
 AC_DEFUN([AC_NETATALK_CONFIG_SUMMARY], [
@@ -26,8 +26,13 @@ AC_DEFUN([AC_NETATALK_CONFIG_SUMMARY], [
        if test "x$netatalk_cv_use_shadowpw" = "xyes"; then
                uams_using_options="$uams_using_options SHADOW"
        fi
-       if test "x$compile_ssl" = "xyes"; then
+       if test "x$neta_cv_compile_dhx" = "xyes"; then
                AC_MSG_RESULT([         DHX     ($uams_using_options)])
+       fi
+        if test "x$neta_cv_compile_dhx2" = "xyes"; then
+                AC_MSG_RESULT([         DHX2    ($uams_using_options)])
+        fi
+       if test "x$neta_cv_have_openssl" = "xyes"; then
                AC_MSG_RESULT([         RANDNUM ($uams_using_options)])
        fi
        if test x"$netatalk_cv_build_krb5_uam" = x"yes"; then
@@ -73,11 +78,16 @@ AC_DEFUN([AC_NETATALK_LIBS_SUMMARY], [
        AC_MSG_RESULT([Using libraries:])
        AC_MSG_RESULT([    LIBS = $LIBS])
        AC_MSG_RESULT([    CFLAGS = $CFLAGS])
-       if test x"$compile_ssl" = x"yes"; then
+       if test x"$neta_cv_have_openssl" = x"yes"; then
                AC_MSG_RESULT([    SSL:])
                AC_MSG_RESULT([        LIBS   = $SSL_LIBS])
                AC_MSG_RESULT([        CFLAGS = $SSL_CFLAGS])
        fi
+        if test x"$neta_cv_have_libgcrypt" = x"yes"; then
+                AC_MSG_RESULT([    LIBGCRYPT:])
+                AC_MSG_RESULT([        LIBS   = $LIBGCRYPT_LIBS])
+                AC_MSG_RESULT([        CFLAGS = $LIBGCRYPT_CFLAGS])
+        fi
        if test x"$netatalk_cv_use_pam" = x"yes"; then
                AC_MSG_RESULT([    PAM:])
                AC_MSG_RESULT([        LIBS   = $PAM_LIBS])