/*
- * $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)
#ifdef UAM_DHX2
+#include <atalk/standards.h>
+
#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 <arpa/inet.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>
#define PRIMEBITS 1024
/* hash a number to a 16-bit quantity */
-#define dhxhash(a) ((((unsigned long) (a) >> 8) ^ \
+#define dhxhash(a) ((((unsigned long) (a) >> 8) ^ \
(unsigned long) (a)) & 0xffff)
/* Some parameters need be maintained across calls */
static gcry_mpi_t serverNonce;
static char *K_MD5hash = NULL;
static int K_hash_len;
-static u_int16_t ID;
+static uint16_t ID;
/* The initialization vectors for CAST128 are fixed by Apple. */
static unsigned char dhx_c2siv[] = { 'L', 'W', 'a', 'l', 'l', 'a', 'c', 'e' };
/*********************************************************
* 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
+ * libgcrypt doesn't provide one directly.
+ * Algorithm taken from GNUTLS:gnutls_dh_primes.c
*********************************************************/
/**
/* 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;
+ LOG(log_info, logtype_uams, "PAM DHX2: libgcrypt versions mismatch. Need: %s", GCRYPT_VERSION);
+ result = AFPERR_MISC;
+ goto error;
}
if (bits < 256)
- qbits = bits / 2;
+ qbits = bits / 2;
else
- qbits = (bits / 40) + 105;
+ qbits = (bits / 40) + 105;
if (qbits & 1) /* better have an even number */
- qbits++;
+ 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++;
+ 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;
+ 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;
+ result = AFPERR_MISC;
+ goto error;
}
-
+
gcry_prime_release_factors (factors);
factors = NULL;
-
+
if (ret_g)
- *ret_g = g;
+ *ret_g = g;
else
- gcry_mpi_release (g);
+ gcry_mpi_release (g);
if (ret_p)
- *ret_p = prime;
+ *ret_p = prime;
else
- gcry_mpi_release (prime);
-
+ gcry_mpi_release (prime);
+
return 0;
error:
return result;
}
-static int dhx2_setup(void *obj, char *ibuf, int ibuflen _U_,
- char *rbuf, int *rbuflen)
+static int dhx2_setup(void *obj, char *ibuf _U_, size_t ibuflen _U_,
+ char *rbuf, size_t *rbuflen)
{
- int i, ret;
-
- unsigned int g_uint;
+ int ret;
+ size_t nwritten;
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 */
+ uint16_t uint16;
+
*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");
+ 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;
+ return AFPERR_NOTAUTH;
/* Initialize DH params */
/* 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;
+ 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;
+ 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);
/* Session ID first */
ID = dhxhash(obj);
- *(u_int16_t *)rbuf = htons(ID);
+ uint16 = htons(ID);
+ memcpy(rbuf, &uint16, sizeof(uint16_t));
rbuf += 2;
*rbuflen += 2;
/* g is next */
- gcry_mpi_print( GCRYMPI_FMT_USG, rbuf, 4, &nwritten, g);
+ 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);
+ 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);
+ uint16 = htons((uint16_t) PRIMEBITS/8);
+ memcpy(rbuf, &uint16, sizeof(uint16_t));
rbuf += 2;
*rbuflen += 2;
/* p */
- gcry_mpi_print( GCRYMPI_FMT_USG, rbuf, PRIMEBITS/8, NULL, 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, rbuf, PRIMEBITS/8, &len, Ma);
- if (len < PRIMEBITS/8) {
- memmove(rbuf + (PRIMEBITS/8) - len, rbuf, len);
- memset(rbuf, 0, (PRIMEBITS/8) - len);
+ 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 */
+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);
/* -------------------------------- */
static int login(void *obj, char *username, int ulen, struct passwd **uam_pwd _U_,
- char *ibuf, int ibuflen,
- char *rbuf, int *rbuflen)
+ char *ibuf, size_t ibuflen,
+ char *rbuf, size_t *rbuflen)
{
if (( dhxpwd = uam_getname(obj, username, ulen)) == NULL ) {
LOG(log_info, logtype_uams, "DHX2: unknown username");
- return AFPERR_PARAM;
+ return AFPERR_NOTAUTH;
}
LOG(log_info, logtype_uams, "DHX2 login: %s", username);
/* 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 *ibuf, size_t ibuflen,
+ char *rbuf, size_t *rbuflen)
{
char *username;
- int len, ulen;
+ size_t len, ulen;
*rbuflen = 0;
/* ----------------------------- */
static int passwd_login_ext(void *obj, char *uname, struct passwd **uam_pwd,
- char *ibuf, int ibuflen,
- char *rbuf, int *rbuflen)
+ char *ibuf, size_t ibuflen,
+ char *rbuf, size_t *rbuflen)
{
char *username;
- int len, ulen;
- u_int16_t temp16;
+ size_t len, ulen;
+ uint16_t temp16;
*rbuflen = 0;
/* -------------------------------- */
-static int logincont1(void *obj, struct passwd **uam_pwd,
- char *ibuf, int ibuflen,
- char *rbuf, int *rbuflen)
+static int logincont1(void *obj _U_, struct passwd **uam_pwd _U_,
+ char *ibuf, size_t ibuflen,
+ char *rbuf, size_t *rbuflen)
{
- u_int16_t retID;
size_t nwritten;
int ret;
- *rbuflen = 0;
-
gcry_mpi_t Mb, K, clientNonce;
- char *K_bin = NULL;
+ unsigned char *K_bin = NULL;
char serverNonce_bin[16];
gcry_cipher_hd_t ctx;
gcry_error_t ctxerror;
+ uint16_t uint16;
+
+ *rbuflen = 0;
Mb = gcry_mpi_new(0);
K = 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");
+ LOG(log_error, logtype_uams, "DHX2: Paket length not correct");
ret = AFPERR_PARAM;
- goto error_noctx;
+ goto error_noctx;
}
/* Skip session id */
/* 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;
+ 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);
+ 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;
+ ret = AFPERR_MISC;
+ free(K_bin);
+ K_bin = NULL;
+ goto error_noctx;
}
gcry_md_hash_buffer(GCRY_MD_MD5, K_MD5hash, K_bin, PRIMEBITS/8);
- free(K_bin);
- K_bin = NULL;
+ 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;
+ 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;
+ 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;
+ 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;
+ ret = AFPERR_MISC;
goto error_ctx;
}
/* Pull out clients nonce */
/* ---- Start building reply packet ---- */
/* Session ID + 1 first */
- *(u_int16_t *)rbuf = htons(ID+1);
+ uint16 = htons(ID+1);
+ memcpy(rbuf, &uint16, sizeof(uint16_t));
rbuf += 2;
*rbuflen += 2;
/* Client nonce + 1 */
- gcry_mpi_print(GCRYMPI_FMT_USG, rbuf, PRIMEBITS/8, NULL, clientNonce);
+ 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;
+ 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;
+ ret = AFPERR_MISC;
+ goto error_ctx;
}
rbuf += 32;
*rbuflen += 32;
return ret;
}
-static int logincont2(void *obj, struct passwd **uam_pwd,
- char *ibuf, int ibuflen,
- char *rbuf, int *rbuflen)
+static int logincont2(void *obj _U_, struct passwd **uam_pwd,
+ char *ibuf, size_t ibuflen,
+ char *rbuf _U_, size_t *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");
+ *rbuflen = 0;
+ retServerNonce = gcry_mpi_new(0);
+
+ /* Packet size should be: Session ID + ServerNonce + Passwd buffer (evantually +10 extra bytes, see Apples Docs)*/
+ if ((ibuflen != 2 + 16 + 256) && (ibuflen != 2 + 16 + 256 + 10)) {
+ LOG(log_error, logtype_uams, "DHX2: Paket length not correct: %d. Should be 274 or 284.", ibuflen);
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) {
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! */
+ /* We're hacked! */
ret = AFPERR_NOTAUTH;
goto error_ctx;
}
- ibuf += 16; /* ibuf now point to passwd in cleartext */
+ 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;
+ *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;
+ goto exit;
}
/* check for expired password */
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;
+ LOG(log_info, logtype_uams, "password for user %s expired", dhxpwd->pw_name);
+ ret = AFPERR_PWDEXPR;
+ goto exit;
}
}
#endif /* SHADOWPW */
}
static int passwd_logincont(void *obj, struct passwd **uam_pwd,
- char *ibuf, int ibuflen,
- char *rbuf, int *rbuflen)
+ char *ibuf, size_t ibuflen,
+ char *rbuf, size_t *rbuflen)
{
- u_int16_t retID;
+ uint16_t retID;
int ret;
/* check for session id */
- retID = ntohs(*(u_int16_t *)ibuf);
+ memcpy(&retID, ibuf, sizeof(uint16_t));
+ retID = ntohs(retID);
if (retID == ID)
ret = logincont1(obj, uam_pwd, ibuf, ibuflen, rbuf, rbuflen);
else if (retID == ID+1)
return ret;
}
-static int uam_setup(const char *path)
+static int uam_setup(void *obj, const char *path)
{
if (uam_register(UAM_SERVER_LOGIN_EXT, path, "DHX2", passwd_login,
passwd_logincont, NULL, passwd_login_ext) < 0)
};
-UAM_MODULE_EXPORT struct uam_export uams_dhx2_pam = {
+UAM_MODULE_EXPORT struct uam_export uams_dhx2_passwd = {
UAM_MODULE_SERVER,
UAM_MODULE_VERSION,
uam_setup, uam_cleanup