]> arthur.barton.de Git - netatalk.git/commitdiff
libgcrypt and DHX2 uam from Frank Lahm
authordidg <didg>
Sat, 22 Nov 2008 12:07:26 +0000 (12:07 +0000)
committerdidg <didg>
Sat, 22 Nov 2008 12:07:26 +0000 (12:07 +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/libgcrpyt.m4 [new file with mode: 0644]
macros/ssl-check.m4
macros/summary.m4

index 4e4a5ef8a1384d532ef3cbd48cc637a9d7b19fc0..9a2959830253ff554275ec44c2c3e9b405585da9 100644 (file)
@@ -2,7 +2,7 @@
 
 pkgconfdir = @PKGCONFDIR@
 
-if USE_DHX
+if HAVE_OPENSSL
 bin_PROGRAMS = afppasswd
 else
 bin_PROGRAMS =
@@ -15,6 +15,6 @@ AM_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
index 8aaec6bd441d048097998986be15b4a776b9ad20..0f14c3e254a4a772b9c012cc4ee9fae6084331f7 100644 (file)
@@ -1,4 +1,4 @@
-dnl $Id: configure.in,v 1.205 2006-09-09 04:30:01 didg Exp $
+dnl $Id: configure.in,v 1.206 2008-11-22 12:07:26 didg Exp $
 dnl configure.in for netatalk
 
 AC_INIT(etc/afpd/main.c)
@@ -919,7 +919,10 @@ if test x"$this_os" = "xtru64"; then
        sysv_style=tru64
 fi
 
-dnl -- look for openssl
+dnl -- check for libgcrypt, if found enables DHX UAM
+NETATALK_CHECK_LIBGCRYPT
+
+dnl -- look for openssl, if found enables DHX UAM and Randnum UAM
 AC_PATH_SSL
 
 dnl -- check for crypt
@@ -931,7 +934,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 +1008,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 d3e35108cc65b98aa1020311bbddd03024fe5ba0..46446a374541cdbc3ab60b5ae150f1a53a26b021 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
-AM_CFLAGS = @CFLAGS@ @SSL_CFLAGS@
+AM_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..bcd858c
--- /dev/null
@@ -0,0 +1,951 @@
+/*
+ * $Id: uams_dhx2_pam.c,v 1.1 2008-11-22 12:07:26 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.
+ * libgcrpyt 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, int ibuflen _U_,
+                     char *rbuf, int *rbuflen)
+{
+    int i, ret;
+
+    unsigned int g_uint;
+    gcry_mpi_t g, Ma;
+    char *Ra_binary = NULL;
+    gcry_cipher_hd_t ctx;
+    gcry_error_t ctxerror;
+
+    const int g_len = 4;
+    size_t len;
+    size_t nwritten;
+
+    *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, 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, rbuf, PRIMEBITS/8, NULL, p);
+    rbuf += PRIMEBITS/8;
+    *rbuflen += PRIMEBITS/8;
+
+    /* Ma */
+    gcry_mpi_print( GCRYMPI_FMT_USG, rbuf, PRIMEBITS/8, &len, Ma);
+    if (len < PRIMEBITS/8) {
+       memmove(rbuf + (PRIMEBITS/8) - len, rbuf, len);
+       memset(rbuf, 0, (PRIMEBITS/8) - len);
+    }
+    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, char *ibuf, int ibuflen, char *rbuf, int *rbuflen)
+{
+    u_int16_t retID;
+    size_t nwritten;
+    int ret;
+    *rbuflen = 0;
+
+    gcry_mpi_t Mb, K, clientNonce;
+    char *K_bin = NULL;
+    char serverNonce_bin[16];
+    gcry_cipher_hd_t ctx;
+    gcry_error_t ctxerror;
+
+    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, 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, 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)
+{
+    unsigned int len;
+    *rbuflen = 0;
+
+    /* Remember it now, use it in changepw_3 */
+    PAM_username = uname;
+    LOG(log_error, logtype_uams, "DHX2 ChangePW: packet 1 processin for user: %s",PAM_username);
+
+    return( dhx2_setup(obj, ibuf, ibuflen, rbuf, rbuflen) );
+}
+
+static int changepw_2(void *obj, 
+                     char *ibuf, int ibuflen, char *rbuf, int *rbuflen)
+{
+    LOG(log_error, logtype_uams, "DHX2 ChangePW: packet 2 processing");
+    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';
+
+    LOG(log_info, logtype_uams, "DHX2 Chgpwd: new pwd \'%s\'",ibuf);
+    LOG(log_info, logtype_uams, "DHX2 Chgpwd: old pwd \'%s\'",ibuf+256);
+
+    /* 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:
+exit:
+    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;
+
+    LOG(log_error, logtype_uams, "DHX2 ChangePW: Start!");
+
+    switch (dhx2_changepw_status) {
+    case 1:
+       ret = changepw_1( obj, uname, ibuf, ibuflen, rbuf, rbuflen);
+       if ( ret == AFPERR_AUTHCONT)
+           dhx2_changepw_status += 1;
+       break;
+    case 2:
+       ret = changepw_2( obj, ibuf, ibuflen, rbuf, rbuflen);
+        if ( ret == AFPERR_AUTHCONT)
+            dhx2_changepw_status += 1;
+       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..925c7ee
--- /dev/null
@@ -0,0 +1,647 @@
+/*
+ * $Id: uams_dhx2_passwd.c,v 1.1 2008-11-22 12:07:26 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.
+ * libgcrpyt 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, int ibuflen _U_,
+                     char *rbuf, int *rbuflen)
+{
+    int i, ret;
+
+    unsigned int g_uint;
+    gcry_mpi_t g, Ma;
+    char *Ra_binary = NULL;
+    gcry_cipher_hd_t ctx;
+    gcry_error_t ctxerror;
+
+    const int g_len = 4;
+    size_t len;
+    size_t nwritten;
+#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, "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, 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, rbuf, PRIMEBITS/8, NULL, p);
+    rbuf += PRIMEBITS/8;
+    *rbuflen += PRIMEBITS/8;
+
+    /* Ma */
+    gcry_mpi_print( GCRYMPI_FMT_USG, rbuf, PRIMEBITS/8, &len, Ma);
+    if (len < PRIMEBITS/8) {
+       memmove(rbuf + (PRIMEBITS/8) - len, rbuf, len);
+       memset(rbuf, 0, (PRIMEBITS/8) - len);
+    }
+    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, struct passwd **uam_pwd,
+                         char *ibuf, int ibuflen,
+                         char *rbuf, int *rbuflen)
+{
+    u_int16_t retID;
+    size_t nwritten;
+    int ret;
+    *rbuflen = 0;
+
+    gcry_mpi_t Mb, K, clientNonce;
+    char *K_bin = NULL;
+    char serverNonce_bin[16];
+    gcry_cipher_hd_t ctx;
+    gcry_error_t ctxerror;
+
+    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, 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, int *rbuflen)
+{
+#ifdef SHADOWPW
+    struct spwd *sp;
+#endif /* SHADOWPW */
+    size_t nwritten;
+    int ret;
+    char *p;
+    *rbuflen = 0;
+    gcry_mpi_t retServerNonce;
+    gcry_cipher_hd_t ctx;
+    gcry_error_t ctxerror;
+
+    /* 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_pam = {
+    UAM_MODULE_SERVER,
+    UAM_MODULE_VERSION,
+    uam_setup, uam_cleanup
+};
+
+#endif /* UAM_DHX2 */
+
diff --git a/macros/libgcrpyt.m4 b/macros/libgcrpyt.m4
new file mode 100644 (file)
index 0000000..599869a
--- /dev/null
@@ -0,0 +1,51 @@
+AC_DEFUN([NETATALK_CHECK_LIBGCRYPT], [
+       AC_ARG_ENABLE(gcrypt, [ --disable-gcrypt    build without gcrypt (limits supported UAMs)])
+       if test "x$enable_gcrypt" != "xno" ; then
+           ac_save_CFLAGS="$CFLAGS"
+           ac_save_LIBS="$LIBS"
+           CFLAGS=""
+           LIBS=""
+           AC_CHECK_LIB([gcrypt], [gcry_cipher_open],, [neta_cv_have_libgcrypt=no])
+           case $host in
+               *-*-darwin*)
+       dnl -- Darwin is a special case: check if version > 1.4.0                                                                                                                                                        
+                 AC_MSG_CHECKING([for correct gcrypt version])
+                 AC_RUN_IFELSE(
+                   [AC_LANG_PROGRAM([
+                       #include <gcrypt.h>                                                                                                                                                                              
+                       #include <stdio.h>],[                                                                                                                                                                            
+                       char*p= GCRYPT_VERSION;
+                       unsigned int vers;
+                       vers=atoi(p)*10000;
+                       p=strchr(p,'.')+1;
+                       vers+=atoi(p)*100;
+                       p=strchr(p,'.')+1;
+                       vers+=atoi(p);
+                       if (vers<10400) return 1;
+                   ])],
+                   [AC_MSG_RESULT([yes])],
+                   [
+                       AC_MSG_ERROR([version is < 1.4.0])
+                       neta_cv_have_libgcrypt=no
+                   ])
+                 ;;
+               *)
+                 ;;
+           esac
+           if test x$neta_cv_have_libgcrypt != xno ; then
+              AC_MSG_RESULT([Enabling UAM: DHX2])
+              AC_DEFINE(LIBGCRYPT_DHX, 1, [Define if the DHX modules should be built with libgcrypt])
+              AC_DEFINE(UAM_DHX2, 1, [Define if the DHX UAM modules should be compiled])
+              neta_cv_have_libgcrypt=yes
+              neta_cv_compile_dhx2=yes
+              LIBGCRYPT_CFLAGS="$CFLAGS"
+              LIBGCRYPT_LIBS="$LIBS"
+              CFLAGS_REMOVE_USR_INCLUDE(LIBGCRYPT_CFLAGS)
+              LIB_REMOVE_USR_LIB(LIBGCRYPT_LIBS)
+              AC_SUBST(LIBGCRYPT_CFLAGS)
+              AC_SUBST(LIBGCRYPT_LIBS)
+          fi
+          CFLAGS="$ac_save_CFLAGS"
+          LIBS="$ac_save_LIBS"
+       fi
+])
\ No newline at end of file
index 6ff4242542673ca7816430ee544d2b9e4b9dc921..9de28019fb5b7473da1091e19ff72bf54135694c 100644 (file)
@@ -1,4 +1,4 @@
-dnl $Id: ssl-check.m4,v 1.13 2005-04-28 20:50:05 bfernhomberg Exp $
+dnl $Id: ssl-check.m4,v 1.14 2008-11-22 12:07:26 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 5f25f915b2fa2eb365acf4af27979970dc159031..3f32834e80325e94b0b32b707419d7b3d4fcd0b3 100644 (file)
@@ -1,4 +1,4 @@
-dnl $Id: summary.m4,v 1.2 2005-04-28 20:50:05 bfernhomberg Exp $
+dnl $Id: summary.m4,v 1.3 2008-11-22 12:07:26 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])