2 * Program to generate MD5 and RSA keys for NTP clients and servers
10 #include <netinfo/ni.h>
14 #include "ntp_stdlib.h"
15 #include "ntp_string.h"
16 #include "ntp_filegen.h"
17 #include "ntp_unixtime.h"
18 #include "ntp_config.h"
19 #include "ntp_cmdargs.h"
23 #include <limits.h> /* PATH_MAX */
27 # include "ntp_crypto.h"
31 # ifdef _POSIX_PATH_MAX
32 # define PATH_MAX _POSIX_PATH_MAX
41 #define MAXKEYLEN 1024 /* maximum encoded key length */
42 #define MODULUSLEN 512 /* length of RSA modulus */
43 #define PRIMELEN 512 /* length of D_H prime, generator */
46 * This program generates (up to) four files:
48 * ntp.keys containing the DES/MD5 private keys,
49 * ntpkey containing the RSA private key,
50 * ntpkey_HOST containing the RSA public key
51 * where HOST is the DNS name of the generating machine,
52 * ntpkey_dh containing the parameters for the Diffie-Hellman
53 * key-agreement algorithm.
55 * The files contain cryptographic values generated by the algorithms of
56 * the rsaref20 package and are in printable ASCII format. Since the
57 * algorythms are seeded by the system clock, each run of this program
58 * will produce a different outcome. There are no options or frills of
59 * any sort, although a number of options would seem to be appropriate.
60 * Waving this program in the breeze will no doubt bring a cast of
61 * thousands to wiggle the options this way and that for various useful
64 * The names of all files begin with "ntp" and end with an extension
65 * consisting of the seconds value of the current NTP timestamp, which
66 * appears in the form ".*". This provides a way to distinguish between
67 * key generations, since the host name and timestamp can be fetched by
68 * a client during operation.
70 * The ntp.keys.* file contains 16 MD5 keys. Each key consists of 16
71 * characters randomized over the ASCII 95-character printing subset.
72 * The file is read by the daemon at the location specified by the keys
73 * configuration file command and made visible only to root. An
74 * additional key consisting of a easily remembered password should be
75 * added by hand for use with the ntpdc program. The file must be
76 * distributed by secure means to other servers and clients sharing the
77 * same security compartment.
79 * The key identifiers for MD5 and DES keys must be less than 65536,
80 * although this program uses only the identifiers from 1 to 16. The key
81 * identifier for each association is specified as the key argument in
82 * the server or peer configuration file command.
84 * The ntpkey.* file contains the RSA private key. It is read by the
85 * daemon at the location specified by the private argument of the
86 * crypto configuration file command and made visible only to root.
87 * This file is useful only to the machine that generated it and never
88 * shared with any other daemon or application program.
90 * The ntpkey_host.* file contains the RSA public key, where host is the
91 * DNS name of the host that generated it. The file is read by the
92 * daemon at the location specified by the public argument to the server
93 * or peer configuration file command. This file can be widely
94 * distributed and stored without using secure means, since the data are
97 * The ntp_dh.* file contains two Diffie-Hellman parameters, the prime
98 * modulus and the generator. The file is read by the daemon at the
99 * location specified by the dhparams argument of the crypto
100 * configuration file command. This file can be widely distributed and
101 * stored without using secure means, since the data are public values.
103 * The file formats all begin with two lines. The first line contains
104 * the file name and decimal timestamp, while the second contains the
105 * readable datestamp. Lines beginning with # are considered comments
106 * and ignored by the daemon. In the ntp.keys.* file, the next 16 lines
107 * contain the MD5 keys in order. In the ntpkey.* and ntpkey_host.*
108 * files, the next line contains the modulus length in bits followed by
109 * the key as a PEM encoded string. In the ntpkey_dh.* file, the next
110 * line contains the prime length in bytes followed by the prime as a
111 * PEM encoded string, and the next and final line contains the
112 * generator length in bytes followed by the generator as a PEM encoded
115 * Note: See the file ./source/rsaref.h in the rsaref20 package for
116 * explanation of return values, if necessary.
120 extern char *config_file;
123 extern struct netinfo_config_state *config_netinfo;
124 extern int check_netinfo;
125 #endif /* HAVE_NETINFO */
128 char *alt_config_file;
130 char config_file_storage[PATH_MAX];
131 char alt_config_file_storage[PATH_MAX];
132 #endif /* SYS_WINNT */
134 int make_dh = 0; /* Make D-H parameter file? */
135 int make_md5 = 0; /* Make MD5 keyfile? */
136 int make_rsa = 0; /* Make RSA pair? */
137 int force = 0; /* Force the installation? */
138 int here = 0; /* Put the files here (curdir)? */
139 int nosymlinks = 0; /* Just create the (timestamped) files? */
140 int memorex = 0; /* Are we live? */
141 int trash = 0; /* Trash old files? */
144 char *f1_keysdir = NTP_KEYSDIR;
146 char *f1_keys; /* Visible MD5 key file name */
147 char *f2_keys; /* timestamped */
148 char *f3_keys; /* previous filename */
163 /* Stubs and hacks so we can link with ntp_config.o */
164 u_long sys_automax; /* maximum session key lifetime */
165 int sys_bclient; /* we set our time to broadcasts */
166 int sys_manycastserver; /* 1 => respond to manycast client pkts */
167 u_long client_limit_period;
168 char * req_file; /* name of the file with configuration info */
169 keyid_t ctl_auth_keyid; /* keyid used for authenticating write requests */
170 struct interface *any_interface; /* default interface */
171 keyid_t info_auth_keyid; /* keyid used to authenticate requests */
172 u_long current_time; /* current time (s) */
173 const char *Version = ""; /* version declaration */
174 keyid_t req_keyid; /* request keyid */
176 u_long client_limit_period;
178 u_long sys_revoke; /* keys revoke timeout */
179 volatile int debug = 0; /* debugging flag */
180 u_char sys_minpoll; /* min poll interval (log2 s) */
182 void snifflink P((const char *, char **));
183 int filep P((const char *));
184 FILE *newfile P((const char *, const char *, mode_t, const char *));
185 void cleanlinks P((const char *, const char *, const char *));
189 struct sockaddr_in *srcadr,
190 struct interface *dstadr,
201 if (debug > 1) printf("peer_config...\n");
213 if (debug > 1) printf("set_sys_var...\n");
221 if (debug > 1) printf("ntp_intres...\n");
228 struct sockaddr_in *raddr,
229 struct interface *linter,
234 if (debug > 1) printf("ctlsettrap...\n");
242 int item, /* configuration item */
243 char *cp /* file name */
248 if (debug > 0) printf("crypto_config: DH/<%d> <%s>\n", item, cp);
249 f1_dhparms = strdup(cp);
251 case CRYPTO_CONF_PRIV:
252 if (debug > 0) printf("crypto_config: PRIVATEKEY/<%d> <%s>\n", item, cp);
253 f1_privatekey = strdup(cp);
255 case CRYPTO_CONF_PUBL:
256 if (debug > 0) printf("crypto_config: PUBLICKEY/<%d> <%s>\n", item, cp);
257 f1_publickey = strdup(cp);
260 if (debug > 1) printf("crypto_config: <%d> <%s>\n", item, cp);
270 struct sockaddr_in *addr
273 if (debug > 1) printf("findinterface...\n");
280 struct sockaddr_in *srcadr,
281 struct refclockstat *in,
282 struct refclockstat *out
285 if (debug > 1) printf("refclock_control...\n");
296 if (debug > 1) printf("loop_config...\n");
309 if (debug > 1) printf("filegen_config...\n");
317 char *invalue /* only one type so far */
320 if (debug > 1) printf("stats_config...\n");
328 struct sockaddr_in *resaddr,
329 struct sockaddr_in *resmask,
334 if (debug > 1) printf("hack_restrict...\n");
342 if (debug > 1) printf("kill_asyncio...\n");
354 if (debug > 1) printf("proto_config...\n");
363 if (debug > 0) printf("getauthkeys: got <%s>\n", keyfile);
364 f1_keys = strdup(keyfile);
374 if (debug > 1) printf("filegen_get...\n");
379 /* End of stubs and hacks */
387 printf("Usage: %s [ -c ntp.conf ] [ -g {d,m,r} ] [ -k key_file ]\n",
389 printf(" [ -d ] [ -f ] [ -h ] [ -l ] [ -n ] [ -t ]\n");
391 printf(" -c /etc/ntp.conf Location of ntp.conf file\n");
392 printf(" -d enable debug messages (can be used multiple times)\n");
393 printf(" -f force installation of generated keys.\n");
394 printf(" -g d Generate D-H parameter file\n");
395 printf(" -g m Generate MD5 key file\n");
396 printf(" -g r Generate RSA keys\n");
397 printf(" -g dmr (Can be combined)\n");
398 printf(" -h Build keys here (current directory). Implies -l\n");
399 printf(" -k key_file Location of key file\n");
400 printf(" -l Don't make the symlinks\n");
401 printf(" -n Don't actually do anything, just say what would be done\n");
402 printf(" -t Trash the (old) files at the end of symlink\n");
416 while ((i = ntp_getopt(argc, argv, "c:dfg:hlnt")) != EOF)
419 config_file = ntp_optarg;
431 while (*ntp_optarg) {
432 switch (*ntp_optarg) {
470 /* If no file type was specified, make them all. */
471 if (!(make_dh | make_md5 | make_rsa)) {
492 rc = readlink(file, buf, sizeof buf);
495 case EINVAL: /* Fall thru */
499 fprintf(stderr, "%s: readlink(%s) failed: (%d) %s\n",
500 progname, file, errno, strerror(errno));
504 *linkdata = strdup(buf);
505 /* XXX: make sure linkdata is not 0... */
506 #endif /* not HAVE_READLINK */
518 if (-1 == stat(fn, &sb)) {
521 fprintf(stderr, "stat(%s) failed: %s\n",
522 fn, strerror(errno));
531 const char *f1, /* Visible file */
532 const char *f2, /* New timestamped file name */
533 mode_t fmask, /* umask for new timestamped file */
534 const char *f3 /* Previous symlink target */
541 if (debug > 1) printf("newfile(%s,%s,%0o,%s)\n", f1, f2,
542 (unsigned)fmask, f3 ? f3 : "NULL");
545 - no symlink support, or
546 - there is no old symlink (!f3)
547 - - file = dirname(f1) / f2
550 - - file = dirname(f3) / f2
552 - - file = dirname(f1) / dirname(f3) / f2
554 print any error message/bail
559 snprintf(fb, sizeof fb, "%s", f2);
568 /* file = dirname(f1) / f2 */
569 snprintf(fb, sizeof fb, "%s", f1);
570 cp = strrchr(fb, '/');
574 snprintf(fb, sizeof fb, "%s/%s", fb, f2);
575 if (debug > 1) printf("case 1: file is <%s>\n", fb);
579 - - file = dirname(f3) / f2
581 - - file = dirname(f1) / dirname(f3) / f2
584 snprintf(fb, sizeof fb, "%s", f1);
585 cp = strrchr(fb, '/');
591 printf("case 2: file is <%s>\n", fb);
595 snprintf(fb, sizeof fb, "%s%s", fb, f3);
596 cp = strrchr(fb, '/');
600 snprintf(fb, sizeof fb, "%s/%s", fb, f2);
601 if (debug > 1) printf("case 3: file is <%s>\n", fb);
607 print any error message/bail
611 printf("Would write file <%s>\n", fb);
616 omask = umask(fmask);
629 const char *f1, /* Visible file */
630 const char *f2, /* New timestamped file name */
631 const char *f3 /* Previous symlink target */
639 Just return if nosymlinks.
641 file = dirname(f3) / f2
644 - if f3 begins with a /, unlink it
645 - else, unlink dirname(f1) / f3
647 #endif /* HAVE_READLINK */
653 printf("Would unlink(%s)\n", f1);
654 else if (unlink(f1)) {
655 if (errno != ENOENT) {
656 fprintf(stderr, "unlink(%s) failed: %s\n", f1,
661 /* file = dirname(f3) / f2 */
663 snprintf(fb, sizeof fb, "%s", f3);
664 cp = strrchr(fb, '/');
674 snprintf(fb, sizeof fb, "%s%s", fb, f2);
675 if (debug > 1) printf("cleanlinks 1: file is <%s>\n", fb);
678 printf("Would symlink <%s> -> <%s>\n", f1, fb);
679 else if (symlink(fb, f1)) {
680 fprintf(stderr, "symlink(%s,%s) failed: %s\n", fb, f1,
687 - if f3 begins with a /, unlink it
688 - else, unlink dirname(f1) / f3
693 printf("Would unlink(%s)\n", f3);
694 else if (unlink(f3)) {
695 if (errno != ENOENT) {
696 fprintf(stderr, "unlink(%s) failed: %s\n", f3,
702 snprintf(fb, sizeof fb, "%s", f1);
703 cp = strrchr(fb, '/');
707 snprintf(fb, sizeof fb, "%s/%s", fb, f3);
709 printf("cleanlinks 2: file is <%s>\n", fb);
711 printf("Would unlink(%s)\n", fb);
712 else if (unlink(fb)) {
713 if (errno != ENOENT) {
714 fprintf(stderr, "unlink(%s) failed: %s\n", fb,
733 R_RSA_PRIVATE_KEY rsaref_private; /* RSA private key */
734 R_RSA_PUBLIC_KEY rsaref_public; /* RSA public key */
735 R_RSA_PROTO_KEY protokey; /* RSA prototype key */
736 R_DH_PARAMS dh_params; /* Diffie-Hellman parameters */
737 R_RANDOM_STRUCT randomstr; /* random structure */
738 int rval; /* return value */
739 u_char encoded_key[MAXKEYLEN]; /* encoded PEM string buffer */
740 u_int modulus; /* modulus length */
743 struct timeval tv; /* initialization vector */
744 u_long ntptime; /* NTP timestamp */
745 char hostname[256]; /* DNS host name */
746 u_char md5key[17]; /* generated MD5 key */
747 FILE *str; /* file handle */
750 mode_t std_mask; /* Standard mask */
751 mode_t sec_mask = 077; /* Secure mask */
752 char pathbuf[PATH_MAX];
754 gethostname(hostname, sizeof(hostname));
755 gettimeofday(&tv, 0);
756 ntptime = tv.tv_sec + JAN_1970;
758 /* Initialize config_file */
759 getconfig(argc, argv); /* ntpd/ntp_config.c */
762 /* Shouldn't happen... */
763 f1_keysdir = "PATH_KEYSDIR";
765 if (*f1_keysdir != '/') {
767 "%s: keysdir path <%s> doesn't begin with a /\n",
768 progname, f1_keysdir);
773 snprintf(pathbuf, sizeof pathbuf, "ntp.keys.%lu",
775 f2_keys = strdup(pathbuf);
778 snprintf(pathbuf, sizeof pathbuf, "%s/ntp.keys",
780 f1_keys = strdup(pathbuf);
782 if (*f1_keys != '/') {
784 "%s: keys path <%s> doesn't begin with a /\n",
788 snifflink(f1_keys, &f3_keys);
791 snprintf(pathbuf, sizeof pathbuf, "ntpkey_%s.%lu",
793 f2_publickey = strdup(pathbuf);
796 snprintf(pathbuf, sizeof pathbuf, "%s/ntpkey_%s",
797 f1_keysdir, hostname);
798 f1_publickey = strdup(pathbuf);
800 if (*f1_publickey != '/') {
802 "%s: publickey path <%s> doesn't begin with a /\n",
803 progname, f1_publickey);
806 snifflink(f1_publickey, &f3_publickey);
808 if (!f2_privatekey) {
809 snprintf(pathbuf, sizeof pathbuf, "ntpkey.%lu",
811 f2_privatekey = strdup(pathbuf);
813 if (!f1_privatekey) {
814 snprintf(pathbuf, sizeof pathbuf, "%s/ntpkey",
816 f1_privatekey = strdup(pathbuf);
818 if (*f1_privatekey != '/') {
820 "%s: privatekey path <%s> doesn't begin with a /\n",
821 progname, f1_privatekey);
824 snifflink(f1_privatekey, &f3_privatekey);
827 snprintf(pathbuf, sizeof pathbuf, "ntpkey_dh.%lu",
829 f2_dhparms = strdup(pathbuf);
832 snprintf(pathbuf, sizeof pathbuf, "%s/ntpkey_dh",
834 f1_dhparms = strdup(pathbuf);
836 if (*f1_dhparms != '/') {
838 "%s: dhparms path <%s> doesn't begin with a /\n",
839 progname, f1_dhparms);
842 snifflink(f1_dhparms, &f3_dhparms);
845 printf("After config:\n");
846 printf("keysdir = <%s>\n", f1_keysdir? f1_keysdir: "");
847 printf("keys = <%s> -> <%s>\n"
848 , f1_keys? f1_keys: ""
849 , f2_keys? f2_keys: ""
851 printf(" old = <%s>\n", f3_keys? f3_keys: "");
852 printf("publickey = <%s> -> <%s>\n"
853 , f1_publickey? f1_publickey: ""
854 , f2_publickey? f2_publickey: ""
856 printf(" old = <%s>\n", f3_publickey? f3_publickey: "");
857 printf("privatekey = <%s> -> <%s>\n"
858 , f1_privatekey? f1_privatekey: ""
859 , f2_privatekey? f2_privatekey: ""
861 printf(" old = <%s>\n", f3_privatekey? f3_privatekey: "");
862 printf("dhparms = <%s> -> <%s>\n"
863 , f1_dhparms? f1_dhparms: ""
864 , f2_dhparms? f2_dhparms: ""
866 printf(" old = <%s>\n", f3_dhparms? f3_dhparms: "");
870 for each file we're going to install:
871 - make the new timestamped file
873 - - remove any old link
876 - - - remove the old file
879 std_mask = umask(sec_mask); /* Get the standard mask */
880 (void) umask(std_mask);
882 if (make_md5 && (force || !filep(f1_keys))) {
884 * Generate 16 random MD5 keys.
886 printf("Generating MD5 key file...\n");
887 str = newfile(f1_keys, f2_keys, sec_mask, f3_keys);
889 srandom((u_int)tv.tv_usec);
890 fprintf(str, "# MD5 key file %s\n# %s", f2_keys,
891 ctime((const time_t *) &tv.tv_sec));
892 for (i = 1; i <= 16; i++) {
893 for (j = 0; j < 16; j++) {
895 temp = random() & 0xff;
897 ** Harlan says Karnaugh maps
898 ** are not his friend, and
899 ** compilers can optimize
904 if (temp > 0x20 && temp < 0x7f)
907 md5key[j] = (u_char)temp;
910 fprintf(str, "%2d M %16s # MD5 key\n",
914 if (str) fclose(str);
915 cleanlinks(f1_keys, f2_keys, f3_keys);
919 if (make_rsa && (force || !filep(f1_publickey)
920 || !filep(f1_privatekey))) {
922 * Roll the RSA public/private key pair.
924 printf("Generating RSA public/private key pair (%d bits)...\n",
927 protokey.bits = MODULUSLEN;
928 protokey.useFermat4 = 1;
929 R_RandomInit(&randomstr);
930 R_GetRandomBytesNeeded(&len, &randomstr);
931 for (i = 0; i < len; i++) {
933 R_RandomUpdate(&randomstr, (u_char *)&temp, 1);
935 rval = R_GeneratePEMKeys(&rsaref_public,
936 &rsaref_private, &protokey,
939 printf("R_GeneratePEMKeys error %x\n", rval);
945 * Generate the file "ntpkey.*" containing the RSA
946 * private key in printable ASCII format.
948 str = newfile(f1_privatekey, f2_privatekey, sec_mask,
951 len = sizeof(rsaref_private)
952 - sizeof(rsaref_private.bits);
953 modulus = (u_int32)rsaref_private.bits;
954 fprintf(str, "# RSA private key file %s\n# %s",
955 f2_privatekey, ctime(&tv.tv_sec));
956 R_EncodePEMBlock(encoded_key, &temp,
957 (u_char *)rsaref_private.modulus,
959 encoded_key[temp] = '\0';
960 fprintf(str, "%d %s\n", modulus, encoded_key);
962 if (str) fclose(str);
963 cleanlinks(f1_privatekey, f2_privatekey, f3_privatekey);
966 * Generate the file "ntpkey_host.*" containing the RSA
967 * public key in printable ASCII format.
969 str = newfile(f1_publickey, f2_publickey, std_mask,
972 len = sizeof(rsaref_public)
973 - sizeof(rsaref_public.bits);
974 modulus = (u_int32)rsaref_public.bits;
975 fprintf(str, "# RSA public key file %s\n# %s",
976 f2_publickey, ctime(&tv.tv_sec));
977 R_EncodePEMBlock(encoded_key, &temp,
978 (u_char *)rsaref_public.modulus, len);
979 encoded_key[temp] = '\0';
980 fprintf(str, "%d %s\n", modulus, encoded_key);
982 if (str) fclose(str);
983 cleanlinks(f1_publickey, f2_publickey, f3_publickey);
988 if (make_dh && (force || !filep(f1_dhparms))) {
990 * Roll the prime and generator for the Diffie-Hellman key
991 * agreement algorithm.
993 printf("Generating Diffie-Hellman parameters (%d bits)...\n",
995 str = newfile(f1_dhparms, f2_dhparms, std_mask, f3_dhparms);
998 R_RandomInit(&randomstr);
999 R_GetRandomBytesNeeded(&len, &randomstr);
1000 for (i = 0; i < len; i++) {
1002 R_RandomUpdate(&randomstr, (u_char *)&temp, 1);
1006 * Generate the file "ntpkey_dh.*" containing the
1007 * Diffie-Hellman prime and generator in printable
1010 len = DH_PRIME_LEN(PRIMELEN);
1011 dh_params.prime = (u_char *)malloc(len);
1012 dh_params.generator = (u_char *)malloc(len);
1013 rval = R_GenerateDHParams(&dh_params, PRIMELEN,
1014 PRIMELEN / 2, &randomstr);
1016 printf("R_GenerateDHParams error %x\n", rval);
1021 "# Diffie-Hellman parameter file %s\n# %s",
1022 f2_dhparms, ctime(&tv.tv_sec));
1023 R_EncodePEMBlock(encoded_key, &temp,
1024 (u_char *)dh_params.prime,
1025 dh_params.primeLen);
1026 encoded_key[temp] = '\0';
1027 fprintf(str, "%d %s\n", dh_params.primeLen,
1029 R_EncodePEMBlock(encoded_key, &temp,
1030 (u_char *)dh_params.generator,
1031 dh_params.generatorLen);
1032 encoded_key[temp] = '\0';
1033 fprintf(str, "%d %s\n", dh_params.generatorLen,
1036 if (str) fclose(str);
1037 cleanlinks(f1_dhparms, f2_dhparms, f3_dhparms);