/*
 * Copyright (c) 2000
 *	Traakan, Inc., Los Altos, CA
 *	All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice unmodified, this list of conditions, and the following
 *    disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

/*
 * Project:  NDMJOB
 * Ident:    $Id: $
 *
 * Description:
 *
 * MD5 authentication support
 ****************************************************************
 * Both sides share a secret in the form of a clear-text
 * password. One side generates a challenge (64-bytes)
 * and conveys it to the other. The other side then
 * uses the challenge, the clear-text password, and
 * the NDMP rules for MD5, and generates a digest.
 * The digest is returned as proof that both sides
 * share the same secret clear-text password.
 *
 * The NDMP rules for MD5 are implemented in ndmmd5_digest().
 * It amounts to positioning the clear-text password and challenge
 * into a "message" buffer, then applying the MD5 algorithm.
 *
 * ndmmd5_generate_challenge() generates a challenge[]
 * using conventional random number routines.
 *
 * ndmmd5_ok_digest() takes a locally known challenge[]
 * and clear-text password, a remotely generated
 * digest[], and determines if everything is correct.
 *
 * Using MD5 prevents clear-text passwords from being conveyed
 * over the network. However, it compels both sides to maintain
 * clear-text passwords in a secure fashion, which is difficult
 * to say the least. Because the NDMP MD5 rules must be followed
 * to digest() the password, it's impractical to consider
 * an external authentication authority.
 *
 * Credits to Rajiv of NetApp for helping with MD5 stuff.
 */


#include "ndmlib.h"
#include "md5.h"


int
ndmmd5_generate_challenge (char challenge[NDMP_MD5_CHALLENGE_LENGTH])
{
	int			i;

	NDMOS_MACRO_SRAND();

	for (i = 0; i < NDMP_MD5_CHALLENGE_LENGTH; i++) {
		challenge[i] = NDMOS_MACRO_RAND() >> (i&7);
	}

	return 0;
}


int
ndmmd5_ok_digest (char challenge[NDMP_MD5_CHALLENGE_LENGTH],
  char *clear_text_password,
  char digest[NDMP_MD5_DIGEST_LENGTH])
{
	char		my_digest[16];
	int		i;

	ndmmd5_digest (challenge, clear_text_password, my_digest);

	for (i = 0; i < NDMP_MD5_DIGEST_LENGTH; i++)
		if (digest[i] != my_digest[i])
			return 0;	/* Invalid */

	return 1;	/* OK */
}


int
ndmmd5_digest (char challenge[NDMP_MD5_CHALLENGE_LENGTH],
  char *clear_text_password,
  char digest[NDMP_MD5_DIGEST_LENGTH])
{
	int		pwlength = strlen (clear_text_password);
	MD5_CTX		mdContext;
	unsigned char	message[128];

	/*
	 * The spec describes the construction of the 128 byte
	 * "message" (probably MD5-speak). It is described as:
	 *
	 *	PASSWORD PADDING CHALLENGE PADDING PASSWORD
	 *
	 * Each PADDING is defined as zeros of length 64 minus pwlen.
	 *
	 * A pwlen of over 32 would result in not all fields
	 * fitting. This begs a question of the order elements
	 * are inserted into the message[]. You get a different
	 * message[] if you insert the PASSWORD(s) before
	 * the CHALLENGE than you get the other way around.
	 *
	 * A pwlen of over 64 would result in PADDING of negative
	 * length, which could cause crash boom bang.
	 *
	 * The resolution of this vaguery implemented here is to
	 * only use the first 32 bytes of the password. All
	 * fields fit. Order dependencies are avoided.
	 *
	 * Final resolution is pending.
	 */
	if (pwlength > 32)
		pwlength = 32;

	/*
	 * Compose the 128-byte buffer according to NDMP rules
	 */
	NDMOS_API_BZERO (message, sizeof message);
	NDMOS_API_BCOPY (clear_text_password, &message[0], pwlength);
	NDMOS_API_BCOPY (clear_text_password,
				&message[128 - pwlength], pwlength);
	NDMOS_API_BCOPY (challenge, &message[64 - pwlength], 64);

	/*
	 * Grind it up, ala MD5
	 */
	MD5Init(&mdContext);
	MD5Update(&mdContext, message, 128);
	MD5Final((unsigned char *)digest, &mdContext);

	/*
	 * ding! done
	 */
	return 0;
}
