ntp-genkeys.c   [plain text]


/*
 * Program to generate MD5 and RSA keys for NTP clients and servers
 */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#ifdef HAVE_NETINFO
#include <netinfo/ni.h>
#endif

#include "ntp_machine.h"
#include "ntpd.h"
#include "ntp_stdlib.h"
#include "ntp_string.h"
#include "ntp_filegen.h"
#include "ntp_unixtime.h"
#include "ntp_config.h"
#include "ntp_cmdargs.h"

#ifndef GETTIMEOFDAY
# define GETTIMEOFDAY gettimeofday
#endif

#include <stdio.h>
#include <unistd.h>
#include <limits.h>		/* PATH_MAX */
#include <sys/stat.h>

#ifdef PUBKEY
# include "ntp_crypto.h"
#endif

#include "l_stdlib.h"

#ifndef PATH_MAX
# ifdef _POSIX_PATH_MAX
#  define PATH_MAX _POSIX_PATH_MAX
# else
#  define PATH_MAX 255
# endif
#endif

/*
 * Cryptodefines
 */
#define MAXKEYLEN	1024	/* maximum encoded key length */
#define MODULUSLEN	512	/* length of RSA modulus */
#define PRIMELEN	512	/* length of D_H prime, generator */

/*
 * This program generates (up to) four files:
 *
 *	ntp.keys    containing the DES/MD5 private keys,
 *	ntpkey      containing the RSA private key,
 *	ntpkey_HOST containing the RSA public key
 *		     where HOST is the DNS name of the generating machine,
 *	ntpkey_dh   containing the parameters for the Diffie-Hellman
 *		    key-agreement algorithm.
 *
 * The files contain cryptographic values generated by the algorithms of
 * the rsaref20 package and are in printable ASCII format.  Since the
 * algorythms are seeded by the system clock, each run of this program
 * will produce a different outcome.  There are no options or frills of
 * any sort, although a number of options would seem to be appropriate.
 * Waving this program in the breeze will no doubt bring a cast of
 * thousands to wiggle the options this way and that for various useful
 * purposes.
 *
 * The names of all files begin with "ntp" and end with an extension
 * consisting of the seconds value of the current NTP timestamp, which
 * appears in the form ".*".  This provides a way to distinguish between
 * key generations, since the host name and timestamp can be fetched by
 * a client during operation.
 *
 * The ntp.keys.* file contains 16 MD5 keys.  Each key consists of 16
 * characters randomized over the ASCII 95-character printing subset.
 * The file is read by the daemon at the location specified by the keys
 * configuration file command and made visible only to root.  An
 * additional key consisting of a easily remembered password should be
 * added by hand for use with the ntpdc program.  The file must be
 * distributed by secure means to other servers and clients sharing the
 * same security compartment.
 *
 * The key identifiers for MD5 and DES keys must be less than 65536,
 * although this program uses only the identifiers from 1 to 16.  The key
 * identifier for each association is specified as the key argument in
 * the server or peer configuration file command.
 *
 * The ntpkey.* file contains the RSA private key.  It is read by the
 * daemon at the location specified by the private argument of the
 * crypto configuration file command and made visible only to root.
 * This file is useful only to the machine that generated it and never
 * shared with any other daemon or application program.
 *
 * The ntpkey_host.* file contains the RSA public key, where host is the
 * DNS name of the host that generated it.  The file is read by the
 * daemon at the location specified by the public argument to the server
 * or peer configuration file command.  This file can be widely
 * distributed and stored without using secure means, since the data are
 * public values.
 *
 * The ntp_dh.* file contains two Diffie-Hellman parameters, the prime
 * modulus and the generator.  The file is read by the daemon at the
 * location specified by the dhparams argument of the crypto
 * configuration file command.  This file can be widely distributed and
 * stored without using secure means, since the data are public values.
 *
 * The file formats all begin with two lines.  The first line contains
 * the file name and decimal timestamp, while the second contains the
 * readable datestamp.  Lines beginning with # are considered comments
 * and ignored by the daemon.  In the ntp.keys.* file, the next 16 lines
 * contain the MD5 keys in order.  In the ntpkey.* and ntpkey_host.*
 * files, the next line contains the modulus length in bits followed by
 * the key as a PEM encoded string.  In the ntpkey_dh.* file, the next
 * line contains the prime length in bytes followed by the prime as a
 * PEM encoded string, and the next and final line contains the
 * generator length in bytes followed by the generator as a PEM encoded
 * string.
 *
 * Note: See the file ./source/rsaref.h in the rsaref20 package for
 * explanation of return values, if necessary.
 */


extern char *config_file;

#ifdef HAVE_NETINFO
extern struct netinfo_config_state *config_netinfo;
extern int check_netinfo;
#endif /* HAVE_NETINFO */

#ifdef SYS_WINNT
char *alt_config_file;
LPTSTR temp;
char config_file_storage[PATH_MAX];
char alt_config_file_storage[PATH_MAX];
#endif /* SYS_WINNT */

int make_dh = 0;		/* Make D-H parameter file? */
int make_md5 = 0;		/* Make MD5 keyfile? */
int make_rsa = 0;		/* Make RSA pair? */
int force = 0;			/* Force the installation? */
int here = 0;			/* Put the files here (curdir)? */
int nosymlinks = 0;		/* Just create the (timestamped) files? */
int memorex = 0;		/* Are we live? */
int trash = 0;			/* Trash old files? */
int errflag = 0;

char *f1_keysdir = NTP_KEYSDIR;

char *f1_keys;			/* Visible MD5 key file name */
char *f2_keys;			/* timestamped */
char *f3_keys;			/* previous filename */

char *f1_publickey;
char *f2_publickey;
char *f3_publickey;

char *f1_privatekey;
char *f2_privatekey;
char *f3_privatekey;

char *f1_dhparms;
char *f2_dhparms;
char *f3_dhparms;


/* Stubs and hacks so we can link with ntp_config.o */
u_long	sys_automax;		/* maximum session key lifetime */
int	sys_bclient;		/* we set our time to broadcasts */
int	sys_manycastserver;	/* 1 => respond to manycast client pkts */
char *	req_file;		/* name of the file with configuration info */
keyid_t	ctl_auth_keyid;		/* keyid used for authenticating write requests */
struct interface *any_interface;	/* default interface */
keyid_t	info_auth_keyid;	/* keyid used to authenticate requests */
u_long	current_time;		/* current time (s) */
const char *Version = "";	/* version declaration */
keyid_t	req_keyid;		/* request keyid */
u_long	client_limit;
u_long	client_limit_period;
l_fp	sys_revoketime;
u_long	sys_revoke;		/* keys revoke timeout */
volatile int debug = 0;		/* debugging flag */
u_char	sys_minpoll;		/* min poll interval (log2 s) */

void snifflink P((const char *, char **));
int filep P((const char *));
FILE *newfile P((const char *, const char *, mode_t, const char *));
void cleanlinks P((const char *, const char *, const char *));

struct peer *
peer_config(
	struct sockaddr_in *srcadr,
	struct interface *dstadr,
	int hmode,
	int version,
	int minpoll,
	int maxpoll,
	u_int flags,
	int ttl,
	keyid_t key,
	u_char *keystr
	)
{
	if (debug > 1) printf("peer_config...\n");
	return 0;
}


void
set_sys_var(
	char *data,
	u_long size,
	int def
	)
{
	if (debug > 1) printf("set_sys_var...\n");
	return;
}


void
ntp_intres (void)
{
	if (debug > 1) printf("ntp_intres...\n");
	return;
}


int
ctlsettrap(
	struct sockaddr_in *raddr,
	struct interface *linter,
	int traptype,
	int version
	)
{
	if (debug > 1) printf("ctlsettrap...\n");
	return 0;
}


#ifdef PUBKEY
void
crypto_config(
	int item,		/* configuration item */
	char *cp		/* file name */
	)
{
	switch (item) {
	    case CRYPTO_CONF_DH:
		if (debug > 0) printf("crypto_config: DH/<%d> <%s>\n", item, cp);
		f1_dhparms = strdup(cp);
		break;
	    case CRYPTO_CONF_PRIV:
		if (debug > 0) printf("crypto_config: PRIVATEKEY/<%d> <%s>\n", item, cp);
		f1_privatekey = strdup(cp);
		break;
	    case CRYPTO_CONF_PUBL:
		if (debug > 0) printf("crypto_config: PUBLICKEY/<%d> <%s>\n", item, cp);
		f1_publickey = strdup(cp);
		break;
	    default:
		if (debug > 1) printf("crypto_config: <%d> <%s>\n", item, cp);
		break;
	}
	return;
}
#endif


struct interface *
findinterface(
	struct sockaddr_in *addr
	)
{
 	if (debug > 1) printf("findinterface...\n");
	return 0;
}


void
refclock_control(
	struct sockaddr_in *srcadr,
	struct refclockstat *in,
	struct refclockstat *out
	)
{
	if (debug > 1) printf("refclock_control...\n");
	return;
}


void
loop_config(
	int item,
	double freq
	)
{
	if (debug > 1) printf("loop_config...\n");
	return;
}


void
filegen_config(
	FILEGEN *gen,
	char    *basename,
	u_int   type,
	u_int   flag
	)
{
	if (debug > 1) printf("filegen_config...\n");
	return;
}


void
stats_config(
	int item,
	char *invalue	/* only one type so far */
	)
{
	if (debug > 1) printf("stats_config...\n");
	return;
}


void
hack_restrict(
	int op,
	struct sockaddr_in *resaddr,
	struct sockaddr_in *resmask,
	int mflags,
	int flags
	)
{
	if (debug > 1) printf("hack_restrict...\n");
	return;
}


void
kill_asyncio (void)
{
	if (debug > 1) printf("kill_asyncio...\n");
	return;
}


void
proto_config(
	int item,
	u_long value,
	double dvalue
	)
{
	if (debug > 1) printf("proto_config...\n");
	return;
}

void
getauthkeys(
	char *keyfile
	)
{
	if (debug > 0) printf("getauthkeys: got <%s>\n", keyfile);
	f1_keys = strdup(keyfile);
	return;
}


FILEGEN *
filegen_get(
	char *name
	)
{
	if (debug > 1) printf("filegen_get...\n");
	return 0;
}


/* End of stubs and hacks */


static void
usage(
	void
	)
{
	printf("Usage: %s [ -c ntp.conf ] [ -g {d,m,r} ] [ -k key_file ]\n",
	       progname);
	printf("       [ -d ] [ -f ] [ -h ] [ -l ] [ -n ] [ -t ]\n");
	printf(" where:\n");
	printf("  -c /etc/ntp.conf   Location of ntp.conf file\n");
	printf("  -d     enable debug messages (can be used multiple times)\n");
	printf("  -f     force installation of generated keys.\n");
	printf("  -g d   Generate D-H parameter file\n");
	printf("  -g m   Generate MD5 key file\n");
	printf("  -g r   Generate RSA keys\n");
	printf("  -g dmr (Can be combined)\n");
	printf("  -h     Build keys here (current directory). Implies -l\n");
	printf("  -k key_file        Location of key file\n");
	printf("  -l     Don't make the symlinks\n");
	printf("  -n     Don't actually do anything, just say what would be done\n");
	printf("  -t     Trash the (old) files at the end of symlink\n");

	exit(1);
}


void
getCmdOpts (
	int argc,
	char *argv[]
	)
{
	int i;

	while ((i = ntp_getopt(argc, argv, "c:dfg:hlnt")) != EOF)
		switch (i) {
		    case 'c':
			config_file = ntp_optarg;
#ifdef HAVE_NETINFO
			check_netinfo = 0;
#endif
			break;
		    case 'd':
			++debug;
			break;
		    case 'f':
			++force;
			break;
		    case 'g':
			while (*ntp_optarg) {
				switch (*ntp_optarg) {
				    case 'd':
					++make_dh;
					break;
				    case 'm':
					++make_md5;
					break;
				    case 'r':
					++make_rsa;
					break;
				    default:
					++errflag;
					break;
				}
				++ntp_optarg;
			}
			break;
		    case 'h':
			++here;
			++nosymlinks;
			break;
		    case 'l':
			++nosymlinks;
			break;
		    case 'n':
			++memorex;
			break;
		    case 't':
			++trash;
			break;
		    case '?':
			++errflag;
			break;
		}

	if (errflag)
		usage();

	/* If no file type was specified, make them all. */
	if (!(make_dh | make_md5 | make_rsa)) {
		++make_dh;
		++make_md5;
		++make_rsa;
	}
}


void
snifflink(
	const char *file,
	char **linkdata
	)
{
#ifdef HAVE_READLINK
	char buf[PATH_MAX];
	int rc;

	if (!file)
		return;

	rc = readlink(file, buf, sizeof buf);
	if (-1 == rc) {
		switch (errno) {
		    case EINVAL:	/* Fall thru */
		    case ENOENT:
			return;
		}
		fprintf(stderr, "%s: readlink(%s) failed: (%d) %s\n",
			progname, file, errno, strerror(errno));
		exit(1);
	}
	buf[rc] = '\0';
	*linkdata = strdup(buf);
	/* XXX: make sure linkdata is not 0... */
#endif /* not HAVE_READLINK */
	return;
}


int
filep(
	const char *fn
	)
{
	struct stat sb;

	if (-1 == stat(fn, &sb)) {
		if (ENOENT == errno)
			return 0;
		fprintf(stderr, "stat(%s) failed: %s\n",
			fn, strerror(errno));
		exit(1);
	}
	return 1;
}


FILE *
newfile(
	const char *f1,		/* Visible file */
	const char *f2,		/* New timestamped file name */
	mode_t fmask,		/* umask for new timestamped file */
	const char *f3		/* Previous symlink target */
	)
{
	FILE *fp;
	char fb[PATH_MAX];
	char *cp;

	if (debug > 1) printf("newfile(%s,%s,%0o,%s)\n", f1, f2,
			      (unsigned)fmask, f3 ? f3 : "NULL");
	/*
	   If:
	   - no symlink support, or
	   - there is no old symlink (!f3)
	   - - file = dirname(f1) / f2
	   Otherwise:
	   - If ('/' == *f3)
	   - - file = dirname(f3) / f2
	   - else
	   - - file = dirname(f1) / dirname(f3) / f2
	   fopen(file)
	   print any error message/bail
	   return FILE
	*/

	if (here)
		snprintf(fb, sizeof fb, "%s", f2);
	else {
		if (
#ifdef HAVE_READLINK
		    !f3
#else
		    1
#endif
		   ) {
			/* file = dirname(f1) / f2 */
			snprintf(fb, sizeof fb, "%s", f1);
			cp = strrchr(fb, '/');
			if (cp) {
				*cp = 0;
			}
			snprintf(fb, sizeof fb, "%s/%s", fb, f2);
			if (debug > 1) printf("case 1: file is <%s>\n", fb);
		} else {
			/*
			  - If ('/' == *f3)
			  - - file = dirname(f3) / f2
			  - else
			  - - file = dirname(f1) / dirname(f3) / f2
			*/
			if ('/' != *f3) {
				snprintf(fb, sizeof fb, "%s", f1);
				cp = strrchr(fb, '/');
				if (cp) {
					++cp;
					*cp = 0;
				}
				if (debug > 1)
					printf("case 2: file is <%s>\n", fb);
			} else {
				*fb = 0;
			}
			snprintf(fb, sizeof fb, "%s%s", fb, f3);
			cp = strrchr(fb, '/');
			if (cp) {
				*cp = 0;
			}
			snprintf(fb, sizeof fb, "%s/%s", fb, f2);
			if (debug > 1) printf("case 3: file is <%s>\n", fb);
		}
	}

	/*
	  fopen(file)
	  print any error message/bail
	  return FILE
	*/
	if (memorex) {
		printf("Would write file <%s>\n", fb);
		fp = NULL;
	} else {
		mode_t omask;

		omask = umask(fmask);
		fp = fopen(fb, "w");
		(void) umask(omask);
		if (fp == NULL) {
			perror(fb);
			exit(1);
		}
	}
	return fp;
}

void
cleanlinks(
	const char *f1,		/* Visible file */
	const char *f2,		/* New timestamped file name */
	const char *f3		/* Previous symlink target */
	)
{
#ifdef HAVE_READLINK
	char *cp;
	char fb[PATH_MAX];

	/*
	  Just return if nosymlinks.
	  unlink f1
	  file = dirname(f3) / f2
	  symlink file, f1
	  If trash:
	  - if f3 begins with a /, unlink it
	  - else, unlink dirname(f1) / f3
	*/
#endif /* HAVE_READLINK */

	if (nosymlinks)
		return;

	if (memorex)
		printf("Would unlink(%s)\n", f1);
	else if (unlink(f1)) {
		if (errno != ENOENT) {
			fprintf(stderr, "unlink(%s) failed: %s\n", f1,
				strerror(errno));
			return;
		}
	}
	/* file = dirname(f3) / f2 */
	if (f3) {
		snprintf(fb, sizeof fb, "%s", f3);
		cp = strrchr(fb, '/');
		if (cp) {
			++cp;
			*cp = 0;
		} else {
			*fb = 0;
		}
	} else {
		*fb = 0;
	}
	snprintf(fb, sizeof fb, "%s%s", fb, f2);
	if (debug > 1) printf("cleanlinks 1: file is <%s>\n", fb);

	if (memorex)
		printf("Would symlink <%s> -> <%s>\n", f1, fb);
	else if (symlink(fb, f1)) {
		fprintf(stderr, "symlink(%s,%s) failed: %s\n", fb, f1,
			strerror(errno));
		return;
	}

	/*
	  If trash:
	  - if f3 begins with a /, unlink it
	  - else, unlink dirname(f1) / f3
	*/
	if (trash && f3) {
		if ('/' == *f3) {
			if (memorex)
				printf("Would unlink(%s)\n", f3);
			else if (unlink(f3)) {
				if (errno != ENOENT) {
					fprintf(stderr, "unlink(%s) failed: %s\n", f3,
						strerror(errno));
					return;
				}
			}
		} else {
			snprintf(fb, sizeof fb, "%s", f1);
			cp = strrchr(fb, '/');
			if (cp) {
				*cp = 0;
			}
			snprintf(fb, sizeof fb, "%s/%s", fb, f3);
			if (debug > 1)
				printf("cleanlinks 2: file is <%s>\n", fb);
			if (memorex)
				printf("Would unlink(%s)\n", fb);
			else if (unlink(fb)) {
				if (errno != ENOENT) {
					fprintf(stderr, "unlink(%s) failed: %s\n", fb,
						strerror(errno));
					return;
				}
			}
		}
	}

	return;
}


int
main(
	int argc,
	char *argv[]
	)
{
#ifdef PUBKEY
	R_RSA_PRIVATE_KEY rsaref_private; /* RSA private key */
	R_RSA_PUBLIC_KEY rsaref_public;	/* RSA public key */
	R_RSA_PROTO_KEY protokey;	/* RSA prototype key */
	R_DH_PARAMS dh_params;		/* Diffie-Hellman parameters */
	R_RANDOM_STRUCT randomstr;	/* random structure */
	int rval;			/* return value */
	u_char encoded_key[MAXKEYLEN];	/* encoded PEM string buffer */
	u_int modulus;			/* modulus length */
	u_int len;
#endif /* PUBKEY */
	struct timeval tv;		/* initialization vector */
	u_long ntptime;			/* NTP timestamp */
	char hostname[256];		/* DNS host name */
	u_char md5key[17];		/* generated MD5 key */ 
	FILE *str;			/* file handle */
	u_int temp;
	int i, j;
	mode_t std_mask;	/* Standard mask */
	mode_t sec_mask = 077;	/* Secure mask */
	char pathbuf[PATH_MAX];

	gethostname(hostname, sizeof(hostname));
	GETTIMEOFDAY(&tv, 0);
	ntptime = tv.tv_sec + JAN_1970;

	/* Initialize config_file */
	getconfig(argc, argv);	/* ntpd/ntp_config.c */

	if (!f1_keysdir) {
		/* Shouldn't happen... */
		f1_keysdir = "PATH_KEYSDIR";
	}
	if (*f1_keysdir != '/') {
		fprintf(stderr,
			"%s: keysdir path <%s> doesn't begin with a /\n",
			progname, f1_keysdir);
		exit(1);
	}

	if (!f2_keys) {
		snprintf(pathbuf, sizeof pathbuf, "ntp.keys.%lu",
			 ntptime);
		f2_keys = strdup(pathbuf);
	}
	if (!f1_keys) {
		snprintf(pathbuf, sizeof pathbuf, "%s/ntp.keys",
			 f1_keysdir);
		f1_keys = strdup(pathbuf);
	}
	if (*f1_keys != '/') {
		fprintf(stderr,
			"%s: keys path <%s> doesn't begin with a /\n",
			progname, f1_keys);
		exit(1);
	}
	snifflink(f1_keys, &f3_keys);

	if (!f2_publickey) {
		snprintf(pathbuf, sizeof pathbuf, "ntpkey_%s.%lu",
			 hostname, ntptime);
		f2_publickey = strdup(pathbuf);
	}
	if (!f1_publickey) {
		snprintf(pathbuf, sizeof pathbuf, "%s/ntpkey_%s",
			 f1_keysdir, hostname);
		f1_publickey = strdup(pathbuf);
	}
	if (*f1_publickey != '/') {
		fprintf(stderr,
			"%s: publickey path <%s> doesn't begin with a /\n",
			progname, f1_publickey);
		exit(1);
	}
	snifflink(f1_publickey, &f3_publickey);

	if (!f2_privatekey) {
		snprintf(pathbuf, sizeof pathbuf, "ntpkey.%lu",
			 ntptime);
		f2_privatekey = strdup(pathbuf);
	}
	if (!f1_privatekey) {
		snprintf(pathbuf, sizeof pathbuf, "%s/ntpkey",
			 f1_keysdir);
		f1_privatekey = strdup(pathbuf);
	}
	if (*f1_privatekey != '/') {
		fprintf(stderr,
			"%s: privatekey path <%s> doesn't begin with a /\n",
			progname, f1_privatekey);
		exit(1);
	}
	snifflink(f1_privatekey, &f3_privatekey);

	if (!f2_dhparms) {
		snprintf(pathbuf, sizeof pathbuf, "ntpkey_dh.%lu",
			 ntptime);
		f2_dhparms = strdup(pathbuf);
	}
	if (!f1_dhparms) {
		snprintf(pathbuf, sizeof pathbuf, "%s/ntpkey_dh",
			 f1_keysdir);
		f1_dhparms = strdup(pathbuf);
	}
	if (*f1_dhparms != '/') {
		fprintf(stderr,
			"%s: dhparms path <%s> doesn't begin with a /\n",
			progname, f1_dhparms);
		exit(1);
	}
	snifflink(f1_dhparms, &f3_dhparms);

	if (debug > 1) {
		printf("After config:\n");
		printf("keysdir    = <%s>\n", f1_keysdir? f1_keysdir: "");
		printf("keys       = <%s> -> <%s>\n"
		       , f1_keys? f1_keys: ""
		       , f2_keys? f2_keys: ""
		      );
		printf("       old = <%s>\n", f3_keys? f3_keys: "");
		printf("publickey  = <%s> -> <%s>\n"
		       , f1_publickey? f1_publickey: ""
		       , f2_publickey? f2_publickey: ""
		      );
		printf("       old = <%s>\n", f3_publickey? f3_publickey: "");
		printf("privatekey = <%s> -> <%s>\n"
		       , f1_privatekey? f1_privatekey: ""
		       , f2_privatekey? f2_privatekey: ""
		      );
		printf("       old = <%s>\n", f3_privatekey? f3_privatekey: "");
		printf("dhparms    = <%s> -> <%s>\n"
		       , f1_dhparms? f1_dhparms: ""
		       , f2_dhparms? f2_dhparms: ""
		      );
		printf("       old = <%s>\n", f3_dhparms? f3_dhparms: "");
	}

	/*
	  for each file we're going to install:
	  - make the new timestamped file
	  - if (!nosymlinks)
	  - - remove any old link
	  - - make the link
	  - - if (trash)
	  - - - remove the old file
	*/

	std_mask = umask(sec_mask); /* Get the standard mask */
	(void) umask(std_mask);

	if (make_md5 && (force || !filep(f1_keys))) {
		/*
		 * Generate 16 random MD5 keys.
		 */
		printf("Generating MD5 key file...\n");
		str = newfile(f1_keys, f2_keys, sec_mask, f3_keys);
		if (!memorex) {
			SRANDOM((u_int)tv.tv_usec);
			fprintf(str, "# MD5 key file %s\n# %s", f2_keys,
				ctime((const time_t *) &tv.tv_sec));
			for (i = 1; i <= 16; i++) {
				for (j = 0; j < 16; j++) {
					while (1) {
						temp = RANDOM & 0xff;
						/*
						** Harlan says Karnaugh maps
						** are not his friend, and
						** compilers can optimize
						** this most easily.
						*/
						if (temp == '#')
							continue;
						if (temp > 0x20 && temp < 0x7f)
							break;
					}
					md5key[j] = (u_char)temp;
				}
				md5key[16] = 0;
				fprintf(str, "%2d M %16s	# MD5 key\n",
					i, md5key);
			}
		}
		if (str) fclose(str);
		cleanlinks(f1_keys, f2_keys, f3_keys);
	}

#ifdef PUBKEY
	if (make_rsa && (force || !filep(f1_publickey)
			 || !filep(f1_privatekey))) {
		/*
		 * Roll the RSA public/private key pair.
		 */
		printf("Generating RSA public/private key pair (%d bits)...\n",
		       MODULUSLEN);
		if (!memorex) {
			protokey.bits = MODULUSLEN;
			protokey.useFermat4 = 1;
			R_RandomInit(&randomstr);
			R_GetRandomBytesNeeded(&len, &randomstr);
			for (i = 0; i < len; i++) {
				temp = RANDOM;
				R_RandomUpdate(&randomstr, (u_char *)&temp, 1);
			}
			rval = R_GeneratePEMKeys(&rsaref_public,
						 &rsaref_private, &protokey,
						 &randomstr);
			if (rval) {
				printf("R_GeneratePEMKeys error %x\n", rval);
				return (-1);
			}
		}

		/*
		 * Generate the file "ntpkey.*" containing the RSA
		 * private key in printable ASCII format.
		 */
		str = newfile(f1_privatekey, f2_privatekey, sec_mask,
			      f3_privatekey);
		if (!memorex) {
			len = sizeof(rsaref_private)
			    - sizeof(rsaref_private.bits);
			modulus = (u_int32)rsaref_private.bits;
			fprintf(str, "# RSA private key file %s\n# %s",
				f2_privatekey,  ctime(&tv.tv_sec));
			R_EncodePEMBlock(encoded_key, &temp,
					 (u_char *)rsaref_private.modulus,
					 len);
			encoded_key[temp] = '\0';
			fprintf(str, "%d %s\n", modulus, encoded_key);
		}
		if (str) fclose(str);
		cleanlinks(f1_privatekey, f2_privatekey, f3_privatekey);

		/*
		 * Generate the file "ntpkey_host.*" containing the RSA
		 * public key in printable ASCII format.
		 */
		str = newfile(f1_publickey, f2_publickey, std_mask,
			      f3_publickey);
		if (!memorex) {
			len = sizeof(rsaref_public)
			    - sizeof(rsaref_public.bits);
			modulus = (u_int32)rsaref_public.bits;
			fprintf(str, "# RSA public key file %s\n# %s",
				f2_publickey, ctime(&tv.tv_sec));
			R_EncodePEMBlock(encoded_key, &temp,
					 (u_char *)rsaref_public.modulus, len);
			encoded_key[temp] = '\0';
			fprintf(str, "%d %s\n", modulus, encoded_key);
		}
		if (str) fclose(str);
		cleanlinks(f1_publickey, f2_publickey, f3_publickey);
	}
#endif /* PUBKEY */

#ifdef PUBKEY
	if (make_dh && (force || !filep(f1_dhparms))) {
		/*
		 * Roll the prime and generator for the Diffie-Hellman key
		 * agreement algorithm.
		 */
		printf("Generating Diffie-Hellman parameters (%d bits)...\n",
		       PRIMELEN);
		str = newfile(f1_dhparms, f2_dhparms, std_mask, f3_dhparms);

		if (!memorex) {
			R_RandomInit(&randomstr);
			R_GetRandomBytesNeeded(&len, &randomstr);
			for (i = 0; i < len; i++) {
				temp = RANDOM;
				R_RandomUpdate(&randomstr, (u_char *)&temp, 1);
			}

			/*
			 * Generate the file "ntpkey_dh.*" containing the
			 * Diffie-Hellman prime and generator in printable
			 * ASCII format.
			 */
			len = DH_PRIME_LEN(PRIMELEN);
			dh_params.prime = (u_char *)malloc(len);
			dh_params.generator = (u_char *)malloc(len);
			rval = R_GenerateDHParams(&dh_params, PRIMELEN,
						  PRIMELEN / 2, &randomstr);
			if (rval) {
				printf("R_GenerateDHParams error %x\n", rval);
				return (-1);
			}

			fprintf(str,
				"# Diffie-Hellman parameter file %s\n# %s",
				f2_dhparms, ctime(&tv.tv_sec));
			R_EncodePEMBlock(encoded_key, &temp,
					 (u_char *)dh_params.prime,
					 dh_params.primeLen);
			encoded_key[temp] = '\0';
			fprintf(str, "%d %s\n", dh_params.primeLen,
				encoded_key);
			R_EncodePEMBlock(encoded_key, &temp,
					 (u_char *)dh_params.generator,
					 dh_params.generatorLen);
			encoded_key[temp] = '\0';
			fprintf(str, "%d %s\n", dh_params.generatorLen,
				encoded_key);
		}
		if (str) fclose(str);
		cleanlinks(f1_dhparms, f2_dhparms, f3_dhparms);
	}
#endif /* PUBKEY */

	return (0);
}