Index: head/contrib/ntp/ntpd/ntp_config.c =================================================================== --- head/contrib/ntp/ntpd/ntp_config.c (revision 276070) +++ head/contrib/ntp/ntpd/ntp_config.c (revision 276071) @@ -1,2582 +1,2582 @@ /* * ntp_config.c - read and apply configuration information */ #ifdef HAVE_CONFIG_H # include #endif #ifdef HAVE_NETINFO # include #endif #include "ntpd.h" #include "ntp_io.h" #include "ntp_unixtime.h" #include "ntp_refclock.h" #include "ntp_filegen.h" #include "ntp_stdlib.h" #include #include #include #include #include #ifdef HAVE_SYS_PARAM_H #include #endif #include #ifndef SIGCHLD # define SIGCHLD SIGCLD #endif #if !defined(VMS) # ifdef HAVE_SYS_WAIT_H # include # endif #endif /* VMS */ #ifdef SYS_WINNT # include static HANDLE ResolverThreadHandle = NULL; HANDLE ResolverEventHandle; #else int resolver_pipe_fd[2]; /* used to let the resolver process alert the parent process */ #endif /* SYS_WINNT */ /* * [Bug 467]: Some linux headers collide with CONFIG_PHONE and CONFIG_KEYS * so #include these later. */ #include "ntp_config.h" #include "ntp_cmdargs.h" extern int priority_done; /* * These routines are used to read the configuration file at * startup time. An entry in the file must fit on a single line. * Entries are processed as multiple tokens separated by white space * Lines are considered terminated when a '#' is encountered. Blank * lines are ignored. */ /* * Translation table - keywords to function index */ struct keyword { const char *text; int keytype; }; /* * Command keywords */ static struct keyword keywords[] = { { "automax", CONFIG_AUTOMAX }, { "broadcast", CONFIG_BROADCAST }, { "broadcastclient", CONFIG_BROADCASTCLIENT }, { "broadcastdelay", CONFIG_BDELAY }, { "calldelay", CONFIG_CDELAY}, #ifdef OPENSSL { "crypto", CONFIG_CRYPTO }, #endif /* OPENSSL */ { "controlkey", CONFIG_CONTROLKEY }, { "disable", CONFIG_DISABLE }, { "driftfile", CONFIG_DRIFTFILE }, { "enable", CONFIG_ENABLE }, { "end", CONFIG_END }, { "filegen", CONFIG_FILEGEN }, { "fudge", CONFIG_FUDGE }, { "includefile", CONFIG_INCLUDEFILE }, { "keys", CONFIG_KEYS }, { "keysdir", CONFIG_KEYSDIR }, { "logconfig", CONFIG_LOGCONFIG }, { "logfile", CONFIG_LOGFILE }, { "manycastclient", CONFIG_MANYCASTCLIENT }, { "manycastserver", CONFIG_MANYCASTSERVER }, { "multicastclient", CONFIG_MULTICASTCLIENT }, { "peer", CONFIG_PEER }, { "phone", CONFIG_PHONE }, { "pidfile", CONFIG_PIDFILE }, { "discard", CONFIG_DISCARD }, { "requestkey", CONFIG_REQUESTKEY }, { "restrict", CONFIG_RESTRICT }, { "revoke", CONFIG_REVOKE }, { "server", CONFIG_SERVER }, { "setvar", CONFIG_SETVAR }, { "statistics", CONFIG_STATISTICS }, { "statsdir", CONFIG_STATSDIR }, { "tick", CONFIG_ADJ }, { "tinker", CONFIG_TINKER }, { "tos", CONFIG_TOS }, { "trap", CONFIG_TRAP }, { "trustedkey", CONFIG_TRUSTEDKEY }, { "ttl", CONFIG_TTL }, { "", CONFIG_UNKNOWN } }; /* * "peer", "server", "broadcast" modifier keywords */ static struct keyword mod_keywords[] = { { "autokey", CONF_MOD_SKEY }, { "burst", CONF_MOD_BURST }, { "iburst", CONF_MOD_IBURST }, { "key", CONF_MOD_KEY }, { "maxpoll", CONF_MOD_MAXPOLL }, { "minpoll", CONF_MOD_MINPOLL }, { "mode", CONF_MOD_MODE }, /* refclocks */ { "noselect", CONF_MOD_NOSELECT }, { "preempt", CONF_MOD_PREEMPT }, { "true", CONF_MOD_TRUE }, { "prefer", CONF_MOD_PREFER }, { "ttl", CONF_MOD_TTL }, /* NTP peers */ { "version", CONF_MOD_VERSION }, { "dynamic", CONF_MOD_DYNAMIC }, { "", CONFIG_UNKNOWN } }; /* * "restrict" modifier keywords */ static struct keyword res_keywords[] = { { "ignore", CONF_RES_IGNORE }, { "limited", CONF_RES_LIMITED }, { "kod", CONF_RES_DEMOBILIZE }, { "lowpriotrap", CONF_RES_LPTRAP }, { "mask", CONF_RES_MASK }, { "nomodify", CONF_RES_NOMODIFY }, { "nopeer", CONF_RES_NOPEER }, { "noquery", CONF_RES_NOQUERY }, { "noserve", CONF_RES_NOSERVE }, { "notrap", CONF_RES_NOTRAP }, { "notrust", CONF_RES_NOTRUST }, { "ntpport", CONF_RES_NTPPORT }, { "version", CONF_RES_VERSION }, { "", CONFIG_UNKNOWN } }; /* * "trap" modifier keywords */ static struct keyword trap_keywords[] = { { "port", CONF_TRAP_PORT }, { "interface", CONF_TRAP_INTERFACE }, { "", CONFIG_UNKNOWN } }; /* * "fudge" modifier keywords */ static struct keyword fudge_keywords[] = { { "flag1", CONF_FDG_FLAG1 }, { "flag2", CONF_FDG_FLAG2 }, { "flag3", CONF_FDG_FLAG3 }, { "flag4", CONF_FDG_FLAG4 }, { "refid", CONF_FDG_REFID }, /* this mapping should be cleaned up (endianness, \0) - kd 20041031 */ { "stratum", CONF_FDG_STRATUM }, { "time1", CONF_FDG_TIME1 }, { "time2", CONF_FDG_TIME2 }, { "", CONFIG_UNKNOWN } }; /* * "filegen" modifier keywords */ static struct keyword filegen_keywords[] = { { "disable", CONF_FGEN_FLAG_DISABLE }, { "enable", CONF_FGEN_FLAG_ENABLE }, { "file", CONF_FGEN_FILE }, { "link", CONF_FGEN_FLAG_LINK }, { "nolink", CONF_FGEN_FLAG_NOLINK }, { "type", CONF_FGEN_TYPE }, { "", CONFIG_UNKNOWN } }; /* * "type" modifier keywords */ static struct keyword fgen_types[] = { { "age", FILEGEN_AGE }, { "day", FILEGEN_DAY }, { "month", FILEGEN_MONTH }, { "none", FILEGEN_NONE }, { "pid", FILEGEN_PID }, { "week", FILEGEN_WEEK }, { "year", FILEGEN_YEAR }, { "", CONFIG_UNKNOWN} }; /* * "enable", "disable" modifier keywords */ static struct keyword flags_keywords[] = { { "auth", PROTO_AUTHENTICATE }, { "bclient", PROTO_BROADCLIENT }, { "calibrate", PROTO_CAL }, { "kernel", PROTO_KERNEL }, { "monitor", PROTO_MONITOR }, { "ntp", PROTO_NTP }, { "stats", PROTO_FILEGEN }, { "", CONFIG_UNKNOWN } }; /* * "discard" modifier keywords */ static struct keyword discard_keywords[] = { { "average", CONF_DISCARD_AVERAGE }, { "minimum", CONF_DISCARD_MINIMUM }, { "monitor", CONF_DISCARD_MONITOR }, { "", CONFIG_UNKNOWN } }; /* * "tinker" modifier keywords */ static struct keyword tinker_keywords[] = { { "step", CONF_CLOCK_MAX }, { "panic", CONF_CLOCK_PANIC }, { "dispersion", CONF_CLOCK_PHI }, { "stepout", CONF_CLOCK_MINSTEP }, { "allan", CONF_CLOCK_ALLAN }, { "huffpuff", CONF_CLOCK_HUFFPUFF }, { "freq", CONF_CLOCK_FREQ }, { "", CONFIG_UNKNOWN } }; /* * "tos" modifier keywords */ static struct keyword tos_keywords[] = { { "minclock", CONF_TOS_MINCLOCK }, { "maxclock", CONF_TOS_MAXCLOCK }, { "minsane", CONF_TOS_MINSANE }, { "floor", CONF_TOS_FLOOR }, { "ceiling", CONF_TOS_CEILING }, { "cohort", CONF_TOS_COHORT }, { "mindist", CONF_TOS_MINDISP }, { "maxdist", CONF_TOS_MAXDIST }, { "maxhop", CONF_TOS_MAXHOP }, { "beacon", CONF_TOS_BEACON }, { "orphan", CONF_TOS_ORPHAN }, { "", CONFIG_UNKNOWN } }; #ifdef OPENSSL /* * "crypto" modifier keywords */ static struct keyword crypto_keywords[] = { { "cert", CONF_CRYPTO_CERT }, { "gqpar", CONF_CRYPTO_GQPAR }, { "host", CONF_CRYPTO_RSA }, { "ident", CONF_CRYPTO_IDENT }, { "iffpar", CONF_CRYPTO_IFFPAR }, { "leap", CONF_CRYPTO_LEAP }, { "mvpar", CONF_CRYPTO_MVPAR }, { "pw", CONF_CRYPTO_PW }, { "randfile", CONF_CRYPTO_RAND }, { "sign", CONF_CRYPTO_SIGN }, { "", CONFIG_UNKNOWN } }; #endif /* OPENSSL */ /* * Address type selection, IPv4 or IPv4. * Used on various lines. */ static struct keyword addr_type[] = { { "-4", CONF_ADDR_IPV4 }, { "-6", CONF_ADDR_IPV6 }, { "", CONFIG_UNKNOWN } }; /* * "logconfig" building blocks */ struct masks { const char *name; unsigned long mask; }; static struct masks logcfg_class[] = { { "clock", NLOG_OCLOCK }, { "peer", NLOG_OPEER }, { "sync", NLOG_OSYNC }, { "sys", NLOG_OSYS }, { (char *)0, 0 } }; static struct masks logcfg_item[] = { { "info", NLOG_INFO }, { "allinfo", NLOG_SYSINFO|NLOG_PEERINFO|NLOG_CLOCKINFO|NLOG_SYNCINFO }, { "events", NLOG_EVENT }, { "allevents", NLOG_SYSEVENT|NLOG_PEEREVENT|NLOG_CLOCKEVENT|NLOG_SYNCEVENT }, { "status", NLOG_STATUS }, { "allstatus", NLOG_SYSSTATUS|NLOG_PEERSTATUS|NLOG_CLOCKSTATUS|NLOG_SYNCSTATUS }, { "statistics", NLOG_STATIST }, { "allstatistics", NLOG_SYSSTATIST|NLOG_PEERSTATIST|NLOG_CLOCKSTATIST|NLOG_SYNCSTATIST }, { "allclock", (NLOG_INFO|NLOG_STATIST|NLOG_EVENT|NLOG_STATUS)<name) { if (strncmp(*s, m->name, strlen(m->name)) == 0) { *s += strlen(m->name); return m->mask; } else { m++; } } return 0; } /* * get_match - find logmask value */ static unsigned long get_match( char *s, struct masks *m ) { while (m->name) { if (strcmp(s, m->name) == 0) { return m->mask; } else { m++; } } return 0; } /* * get_logmask - build bitmask for ntp_syslogmask */ static unsigned long get_logmask( char *s ) { char *t; unsigned long offset; unsigned long mask; t = s; offset = get_pfxmatch(&t, logcfg_class); mask = get_match(t, logcfg_item); if (mask) return mask << offset; else msyslog(LOG_ERR, "logconfig: illegal argument %s - ignored", s); return 0; } /* * getconfig - get command line options and read the configuration file */ void getconfig( int argc, char *argv[] ) { register int i; int c; int errflg; int status; int istart; int peerversion; int minpoll; int maxpoll; int ttl; long stratum; unsigned long ul; keyid_t peerkey; u_char *peerkeystr; u_long fudgeflag; u_int peerflags; int hmode; struct sockaddr_storage peeraddr; struct sockaddr_storage maskaddr; FILE *fp[MAXINCLUDELEVEL+1]; FILE *includefile; int includelevel = 0; char line[MAXLINE]; char *(tokens[MAXTOKENS]); int ntokens = 0; int tok = CONFIG_UNKNOWN; struct interface *localaddr; struct refclockstat clock_stat; FILEGEN *filegen; /* * Initialize, initialize */ errflg = 0; #ifndef SYS_WINNT config_file = CONFIG_FILE; #else temp = CONFIG_FILE; if (!ExpandEnvironmentStrings((LPCTSTR)temp, (LPTSTR)config_file_storage, (DWORD)sizeof(config_file_storage))) { msyslog(LOG_ERR, "ExpandEnvironmentStrings CONFIG_FILE failed: %m\n"); exit(1); } config_file = config_file_storage; temp = ALT_CONFIG_FILE; if (!ExpandEnvironmentStrings((LPCTSTR)temp, (LPTSTR)alt_config_file_storage, (DWORD)sizeof(alt_config_file_storage))) { msyslog(LOG_ERR, "ExpandEnvironmentStrings ALT_CONFIG_FILE failed: %m\n"); exit(1); } alt_config_file = alt_config_file_storage; #endif /* SYS_WINNT */ res_fp = NULL; ntp_syslogmask = NLOG_SYNCMASK; /* set more via logconfig */ /* * install a non default variable with this daemon version */ (void) sprintf(line, "daemon_version=\"%s\"", Version); set_sys_var(line, strlen(line)+1, RO); /* * Say how we're setting the time of day */ (void) sprintf(line, "settimeofday=\"%s\"", set_tod_using); set_sys_var(line, strlen(line)+1, RO); /* * Initialize the loop. */ loop_config(LOOP_DRIFTINIT, 0.); getCmdOpts(argc, argv); if ( (fp[0] = fopen(FindConfig(config_file), "r")) == NULL #ifdef HAVE_NETINFO /* If there is no config_file, try NetInfo. */ && check_netinfo && !(config_netinfo = get_netinfo_config()) #endif /* HAVE_NETINFO */ ) { fprintf(stderr, "getconfig: Couldn't open <%s>\n", FindConfig(config_file)); msyslog(LOG_INFO, "getconfig: Couldn't open <%s>", FindConfig(config_file)); #ifdef SYS_WINNT /* Under WinNT try alternate_config_file name, first NTP.CONF, then NTP.INI */ if ((fp[0] = fopen(FindConfig(alt_config_file), "r")) == NULL) { /* * Broadcast clients can sometimes run without * a configuration file. */ fprintf(stderr, "getconfig: Couldn't open <%s>\n", FindConfig(alt_config_file)); msyslog(LOG_INFO, "getconfig: Couldn't open <%s>", FindConfig(alt_config_file)); return; } #else /* not SYS_WINNT */ return; #endif /* not SYS_WINNT */ } proto_config(PROTO_MONITOR, 0, 0., NULL); for (;;) { if (tok == CONFIG_END) break; if (fp[includelevel]) tok = gettokens(fp[includelevel], line, tokens, &ntokens); #ifdef HAVE_NETINFO else tok = gettokens_netinfo(config_netinfo, tokens, &ntokens); #endif /* HAVE_NETINFO */ if (tok == CONFIG_UNKNOWN) { if (includelevel > 0) { fclose(fp[includelevel--]); continue; } else { break; } } switch(tok) { case CONFIG_PEER: case CONFIG_SERVER: case CONFIG_MANYCASTCLIENT: case CONFIG_BROADCAST: if (tok == CONFIG_PEER) hmode = MODE_ACTIVE; else if (tok == CONFIG_SERVER) hmode = MODE_CLIENT; else if (tok == CONFIG_MANYCASTCLIENT) hmode = MODE_CLIENT; else hmode = MODE_BROADCAST; if (ntokens < 2) { msyslog(LOG_ERR, "No address for %s, line ignored", tokens[0]); break; } istart = 1; memset((char *)&peeraddr, 0, sizeof(peeraddr)); peeraddr.ss_family = default_ai_family; switch (matchkey(tokens[istart], addr_type, 0)) { case CONF_ADDR_IPV4: peeraddr.ss_family = AF_INET; istart++; break; case CONF_ADDR_IPV6: peeraddr.ss_family = AF_INET6; istart++; break; } status = getnetnum(tokens[istart], &peeraddr, 0, t_UNK); if (status == -1) break; /* Found IPv6 address */ if(status != 1) { errflg = -1; } else { errflg = 0; if ( #ifdef REFCLOCK !ISREFCLOCKADR(&peeraddr) && #endif ISBADADR(&peeraddr)) { msyslog(LOG_ERR, "attempt to configure invalid address %s", stoa(&peeraddr)); break; } /* * Shouldn't be able to specify multicast * address for server/peer! * and unicast address for manycastclient! */ if (peeraddr.ss_family == AF_INET) { if (((tok == CONFIG_SERVER) || (tok == CONFIG_PEER)) && #ifdef REFCLOCK !ISREFCLOCKADR(&peeraddr) && #endif IN_CLASSD(ntohl(((struct sockaddr_in*)&peeraddr)->sin_addr.s_addr))) { msyslog(LOG_ERR, "attempt to configure invalid address %s", stoa(&peeraddr)); break; } if ((tok == CONFIG_MANYCASTCLIENT) && !IN_CLASSD(ntohl(((struct sockaddr_in*)&peeraddr)->sin_addr.s_addr))) { msyslog(LOG_ERR, "attempt to configure invalid address %s", stoa(&peeraddr)); break; } } else if(peeraddr.ss_family == AF_INET6) { if (((tok == CONFIG_SERVER) || (tok == CONFIG_PEER)) && #ifdef REFCLOCK !ISREFCLOCKADR(&peeraddr) && #endif IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6*)&peeraddr)->sin6_addr)) { msyslog(LOG_ERR, "attempt to configure in valid address %s", stoa(&peeraddr)); break; } if ((tok == CONFIG_MANYCASTCLIENT) && !IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6*)&peeraddr)->sin6_addr)) { msyslog(LOG_ERR, "attempt to configure in valid address %s", stoa(&peeraddr)); break; } } } if (peeraddr.ss_family == AF_INET6 && isc_net_probeipv6() != ISC_R_SUCCESS) break; peerversion = NTP_VERSION; minpoll = NTP_MINDPOLL; maxpoll = NTP_MAXDPOLL; peerkey = 0; peerkeystr = (u_char *)"*"; peerflags = 0; ttl = 0; istart++; for (i = istart; i < ntokens; i++) switch (matchkey(tokens[i], mod_keywords, 1)) { case CONF_MOD_VERSION: if (i >= ntokens-1) { msyslog(LOG_ERR, "peer/server version requires an argument"); errflg = 1; break; } peerversion = atoi(tokens[++i]); if ((u_char)peerversion > NTP_VERSION || (u_char)peerversion < NTP_OLDVERSION) { msyslog(LOG_ERR, "inappropriate version number %s, line ignored", tokens[i]); errflg = 1; } break; case CONF_MOD_KEY: if (i >= ntokens-1) { msyslog(LOG_ERR, "key: argument required"); errflg = 1; break; } peerkey = (int)atol(tokens[++i]); peerflags |= FLAG_AUTHENABLE; break; case CONF_MOD_MINPOLL: if (i >= ntokens-1) { msyslog(LOG_ERR, "minpoll: argument required"); errflg = 1; break; } minpoll = atoi(tokens[++i]); if (minpoll < NTP_MINPOLL) { msyslog(LOG_INFO, "minpoll: provided value (%d) is below minimum (%d)", minpoll, NTP_MINPOLL); minpoll = NTP_MINPOLL; } break; case CONF_MOD_MAXPOLL: if (i >= ntokens-1) { msyslog(LOG_ERR, "maxpoll: argument required" ); errflg = 1; break; } maxpoll = atoi(tokens[++i]); if (maxpoll > NTP_MAXPOLL) { msyslog(LOG_INFO, "maxpoll: provided value (%d) is above maximum (%d)", maxpoll, NTP_MAXPOLL); maxpoll = NTP_MAXPOLL; } break; case CONF_MOD_PREFER: peerflags |= FLAG_PREFER; break; case CONF_MOD_PREEMPT: peerflags |= FLAG_PREEMPT; break; case CONF_MOD_NOSELECT: peerflags |= FLAG_NOSELECT; break; case CONF_MOD_TRUE: peerflags |= FLAG_TRUE; case CONF_MOD_BURST: peerflags |= FLAG_BURST; break; case CONF_MOD_IBURST: peerflags |= FLAG_IBURST; break; case CONF_MOD_DYNAMIC: msyslog(LOG_WARNING, "Warning: the \"dynamic\" keyword has been obsoleted" " and will be removed in the next release\n"); break; #ifdef OPENSSL case CONF_MOD_SKEY: peerflags |= FLAG_SKEY | FLAG_AUTHENABLE; break; #endif /* OPENSSL */ case CONF_MOD_TTL: if (i >= ntokens-1) { msyslog(LOG_ERR, "ttl: argument required"); errflg = 1; break; } ttl = atoi(tokens[++i]); if (ttl >= MAX_TTL) { msyslog(LOG_ERR, "ttl: invalid argument"); errflg = 1; } break; case CONF_MOD_MODE: if (i >= ntokens-1) { msyslog(LOG_ERR, "mode: argument required"); errflg = 1; break; } ttl = atoi(tokens[++i]); break; case CONFIG_UNKNOWN: errflg = 1; break; } if (minpoll > maxpoll) { msyslog(LOG_ERR, "config error: minpoll > maxpoll"); errflg = 1; } if (errflg == 0) { if (peer_config(&peeraddr, ANY_INTERFACE_CHOOSE(&peeraddr), hmode, peerversion, minpoll, maxpoll, peerflags, ttl, peerkey, peerkeystr) == 0) { msyslog(LOG_ERR, "configuration of %s failed", stoa(&peeraddr)); } } else if (errflg == -1) { save_resolve(tokens[istart - 1], hmode, peerversion, minpoll, maxpoll, peerflags, ttl, peerkey, peerkeystr, peeraddr.ss_family); } break; case CONFIG_DRIFTFILE: if (ntokens >= 2) stats_config(STATS_FREQ_FILE, tokens[1]); else stats_config(STATS_FREQ_FILE, (char *)0); stats_write_period = stats_write_tolerance = 0; if (ntokens >= 3) stats_write_period = 60 * atol(tokens[2]); if (stats_write_period <= 0) stats_write_period = 3600; if (ntokens >= 4) { double ftemp; sscanf(tokens[3], "%lf", &ftemp); stats_write_tolerance = ftemp / 100; } break; case CONFIG_PIDFILE: if (ntokens >= 2) stats_config(STATS_PID_FILE, tokens[1]); else stats_config(STATS_PID_FILE, (char *)0); break; case CONFIG_END: for ( i = 0; i <= includelevel; i++ ) { fclose(fp[i]); } break; case CONFIG_INCLUDEFILE: if (ntokens < 2) { msyslog(LOG_ERR, "includefile needs one argument"); break; } if (includelevel >= MAXINCLUDELEVEL) { fprintf(stderr, "getconfig: Maximum include file level exceeded.\n"); msyslog(LOG_INFO, "getconfig: Maximum include file level exceeded."); break; } includefile = fopen(FindConfig(tokens[1]), "r"); if (includefile == NULL) { fprintf(stderr, "getconfig: Couldn't open <%s>\n", FindConfig(tokens[1])); msyslog(LOG_INFO, "getconfig: Couldn't open <%s>", FindConfig(tokens[1])); break; } fp[++includelevel] = includefile; break; case CONFIG_LOGFILE: if (ntokens >= 2) { FILE *new_file; new_file = fopen(tokens[1], "a"); if (new_file != NULL) { NLOG(NLOG_SYSINFO) /* conditional if clause for conditional syslog */ msyslog(LOG_NOTICE, "logging to file %s", tokens[1]); if (syslog_file != NULL && fileno(syslog_file) != fileno(new_file)) (void)fclose(syslog_file); syslog_file = new_file; syslogit = 0; } else msyslog(LOG_ERR, "Cannot open log file %s", tokens[1]); } else msyslog(LOG_ERR, "logfile needs one argument"); break; case CONFIG_LOGCONFIG: for (i = 1; i < ntokens; i++) { int add = 1; int equals = 0; char * s = &tokens[i][0]; switch (*s) { case '+': case '-': case '=': add = *s == '+'; equals = *s == '='; s++; break; default: break; } if (equals) { ntp_syslogmask = get_logmask(s); } else { if (add) { ntp_syslogmask |= get_logmask(s); } else { ntp_syslogmask &= ~get_logmask(s); } } #ifdef DEBUG if (debug) printf("ntp_syslogmask = 0x%08lx (%s)\n", ntp_syslogmask, tokens[i]); #endif } break; case CONFIG_BROADCASTCLIENT: if (ntokens == 1) { proto_config(PROTO_BROADCLIENT, 1, 0., NULL); } else { proto_config(PROTO_BROADCLIENT, 2, 0., NULL); } break; case CONFIG_MULTICASTCLIENT: case CONFIG_MANYCASTSERVER: if (ntokens > 1) { istart = 1; memset((char *)&peeraddr, 0, sizeof(peeraddr)); peeraddr.ss_family = default_ai_family; switch (matchkey(tokens[istart], addr_type, 0)) { case CONF_ADDR_IPV4: peeraddr.ss_family = AF_INET; istart++; break; case CONF_ADDR_IPV6: peeraddr.ss_family = AF_INET6; istart++; break; } /* * Abuse maskaddr to store the prefered ip * version. */ memset((char *)&maskaddr, 0, sizeof(maskaddr)); maskaddr.ss_family = peeraddr.ss_family; for (i = istart; i < ntokens; i++) { memset((char *)&peeraddr, 0, sizeof(peeraddr)); peeraddr.ss_family = maskaddr.ss_family; if (getnetnum(tokens[i], &peeraddr, 1, t_UNK) == 1) proto_config(PROTO_MULTICAST_ADD, 0, 0., &peeraddr); } } else proto_config(PROTO_MULTICAST_ADD, 0, 0., NULL); if (tok == CONFIG_MULTICASTCLIENT) proto_config(PROTO_MULTICAST_ADD, 1, 0., NULL); else if (tok == CONFIG_MANYCASTSERVER) sys_manycastserver = 1; break; case CONFIG_KEYS: if (ntokens >= 2) { getauthkeys(tokens[1]); } break; case CONFIG_KEYSDIR: if (ntokens < 2) { msyslog(LOG_ERR, "Keys directory name required"); break; } keysdir = (char *)emalloc(strlen(tokens[1]) + 1); strcpy(keysdir, tokens[1]); break; case CONFIG_TINKER: for (i = 1; i < ntokens; i++) { int temp; double ftemp; temp = matchkey(tokens[i++], tinker_keywords, 1); if (i > ntokens - 1) { msyslog(LOG_ERR, "tinker: missing argument"); errflg++; break; } sscanf(tokens[i], "%lf", &ftemp); switch(temp) { case CONF_CLOCK_MAX: loop_config(LOOP_MAX, ftemp); break; case CONF_CLOCK_PANIC: loop_config(LOOP_PANIC, ftemp); break; case CONF_CLOCK_PHI: loop_config(LOOP_PHI, ftemp); break; case CONF_CLOCK_MINSTEP: loop_config(LOOP_MINSTEP, ftemp); break; case CONF_CLOCK_ALLAN: loop_config(LOOP_ALLAN, ftemp); break; case CONF_CLOCK_HUFFPUFF: loop_config(LOOP_HUFFPUFF, ftemp); break; case CONF_CLOCK_FREQ: loop_config(LOOP_FREQ, ftemp); break; } } break; case CONFIG_TOS: for (i = 1; i < ntokens; i++) { int temp; double ftemp; temp = matchkey(tokens[i++], tos_keywords, 1); if (i > ntokens - 1) { msyslog(LOG_ERR, "tos: missing argument"); errflg++; break; } sscanf(tokens[i], "%lf", &ftemp); switch(temp) { case CONF_TOS_MINCLOCK: proto_config(PROTO_MINCLOCK, 0, ftemp, NULL); break; case CONF_TOS_MAXCLOCK: proto_config(PROTO_MAXCLOCK, 0, ftemp, NULL); break; case CONF_TOS_MINSANE: proto_config(PROTO_MINSANE, 0, ftemp, NULL); break; case CONF_TOS_FLOOR: proto_config(PROTO_FLOOR, 0, ftemp, NULL); break; case CONF_TOS_CEILING: proto_config(PROTO_CEILING, 0, ftemp, NULL); break; case CONF_TOS_COHORT: proto_config(PROTO_COHORT, 0, ftemp, NULL); break; case CONF_TOS_MINDISP: proto_config(PROTO_MINDISP, 0, ftemp, NULL); break; case CONF_TOS_MAXDIST: proto_config(PROTO_MAXDIST, 0, ftemp, NULL); break; case CONF_TOS_MAXHOP: proto_config(PROTO_MAXHOP, 0, ftemp, NULL); break; case CONF_TOS_ORPHAN: proto_config(PROTO_ORPHAN, 0, ftemp, NULL); break; case CONF_TOS_BEACON: proto_config(PROTO_BEACON, 0, ftemp, NULL); break; } } break; case CONFIG_TTL: for (i = 1; i < ntokens && i < MAX_TTL; i++) { sys_ttl[i - 1] = (u_char) atoi(tokens[i]); sys_ttlmax = i - 1; } break; case CONFIG_DISCARD: for (i = 1; i < ntokens; i++) { int temp; temp = matchkey(tokens[i++], discard_keywords, 1); if (i > ntokens - 1) { msyslog(LOG_ERR, "discard: missing argument"); errflg++; break; } switch(temp) { case CONF_DISCARD_AVERAGE: res_avg_interval = atoi(tokens[i]); break; case CONF_DISCARD_MINIMUM: res_min_interval = atoi(tokens[i]); break; case CONF_DISCARD_MONITOR: mon_age = atoi(tokens[i]); break; default: msyslog(LOG_ERR, "discard: unknown keyword"); break; } } break; #ifdef OPENSSL case CONFIG_REVOKE: if (ntokens >= 2) sys_revoke = (u_char) max(atoi(tokens[1]), KEY_REVOKE); break; case CONFIG_AUTOMAX: if (ntokens >= 2) sys_automax = 1 << max(atoi(tokens[1]), 10); break; case CONFIG_CRYPTO: if (ntokens == 1) { crypto_config(CRYPTO_CONF_NONE, NULL); break; } for (i = 1; i < ntokens; i++) { int temp; temp = matchkey(tokens[i++], crypto_keywords, 1); if (i > ntokens - 1) { msyslog(LOG_ERR, "crypto: missing argument"); errflg++; break; } switch(temp) { case CONF_CRYPTO_CERT: crypto_config(CRYPTO_CONF_CERT, tokens[i]); break; case CONF_CRYPTO_RSA: crypto_config(CRYPTO_CONF_PRIV, tokens[i]); break; case CONF_CRYPTO_IDENT: crypto_config(CRYPTO_CONF_IDENT, tokens[i]); break; case CONF_CRYPTO_IFFPAR: crypto_config(CRYPTO_CONF_IFFPAR, tokens[i]); break; case CONF_CRYPTO_GQPAR: crypto_config(CRYPTO_CONF_GQPAR, tokens[i]); break; case CONF_CRYPTO_MVPAR: crypto_config(CRYPTO_CONF_MVPAR, tokens[i]); break; case CONF_CRYPTO_LEAP: crypto_config(CRYPTO_CONF_LEAP, tokens[i]); break; case CONF_CRYPTO_PW: crypto_config(CRYPTO_CONF_PW, tokens[i]); break; case CONF_CRYPTO_RAND: crypto_config(CRYPTO_CONF_RAND, tokens[i]); break; case CONF_CRYPTO_SIGN: crypto_config(CRYPTO_CONF_SIGN, tokens[i]); break; default: msyslog(LOG_ERR, "crypto: unknown keyword"); break; } } break; #endif /* OPENSSL */ case CONFIG_RESTRICT: if (ntokens < 2) { msyslog(LOG_ERR, "restrict requires an address"); break; } istart = 1; memset((char *)&peeraddr, 0, sizeof(peeraddr)); peeraddr.ss_family = default_ai_family; switch (matchkey(tokens[istart], addr_type, 0)) { case CONF_ADDR_IPV4: peeraddr.ss_family = AF_INET; istart++; break; case CONF_ADDR_IPV6: peeraddr.ss_family = AF_INET6; istart++; break; } /* * Assume default means an IPv4 address, except * if forced by a -4 or -6. */ if (STREQ(tokens[istart], "default")) { if (peeraddr.ss_family == 0) peeraddr.ss_family = AF_INET; } else if (getnetnum(tokens[istart], &peeraddr, 1, t_UNK) != 1) break; /* * Use peerversion as flags, peerkey as mflags. Ick. */ peerversion = 0; peerkey = 0; errflg = 0; SET_HOSTMASK(&maskaddr, peeraddr.ss_family); istart++; for (i = istart; i < ntokens; i++) { switch (matchkey(tokens[i], res_keywords, 1)) { case CONF_RES_MASK: if (i >= ntokens-1) { msyslog(LOG_ERR, "mask keyword needs argument"); errflg++; break; } i++; if (getnetnum(tokens[i], &maskaddr, 1, t_MSK) != 1) errflg++; break; case CONF_RES_IGNORE: peerversion |= RES_IGNORE; break; case CONF_RES_NOSERVE: peerversion |= RES_DONTSERVE; break; case CONF_RES_NOTRUST: peerversion |= RES_DONTTRUST; break; case CONF_RES_NOQUERY: peerversion |= RES_NOQUERY; break; case CONF_RES_NOMODIFY: peerversion |= RES_NOMODIFY; break; case CONF_RES_NOPEER: peerversion |= RES_NOPEER; break; case CONF_RES_NOTRAP: peerversion |= RES_NOTRAP; break; case CONF_RES_LPTRAP: peerversion |= RES_LPTRAP; break; case CONF_RES_NTPPORT: peerkey |= RESM_NTPONLY; break; case CONF_RES_VERSION: peerversion |= RES_VERSION; break; case CONF_RES_DEMOBILIZE: peerversion |= RES_DEMOBILIZE; break; case CONF_RES_LIMITED: peerversion |= RES_LIMITED; break; case CONFIG_UNKNOWN: errflg++; break; } } if (SOCKNUL(&peeraddr)) ANYSOCK(&maskaddr); if (!errflg) hack_restrict(RESTRICT_FLAGS, &peeraddr, &maskaddr, (int)peerkey, peerversion); break; case CONFIG_BDELAY: if (ntokens >= 2) { double tmp; if (sscanf(tokens[1], "%lf", &tmp) != 1) { msyslog(LOG_ERR, "broadcastdelay value %s undecodable", tokens[1]); } else { proto_config(PROTO_BROADDELAY, 0, tmp, NULL); } } break; case CONFIG_CDELAY: if (ntokens >= 2) { u_long ui; if (sscanf(tokens[1], "%ld", &ui) != 1) msyslog(LOG_ERR, "illegal value - line ignored"); else proto_config(PROTO_CALLDELAY, ui, 0, NULL); } break; case CONFIG_TRUSTEDKEY: for (i = 1; i < ntokens; i++) { keyid_t tkey; tkey = atol(tokens[i]); if (tkey == 0) { msyslog(LOG_ERR, "trusted key %s unlikely", tokens[i]); } else { authtrust(tkey, 1); } } break; case CONFIG_REQUESTKEY: if (ntokens >= 2) { if (!atouint(tokens[1], &ul)) { msyslog(LOG_ERR, "%s is undecodable as request key", tokens[1]); } else if (ul == 0) { msyslog(LOG_ERR, "%s makes a poor request keyid", tokens[1]); } else { #ifdef DEBUG if (debug > 3) printf( "set info_auth_key to %08lx\n", ul); #endif info_auth_keyid = (keyid_t)ul; } } break; case CONFIG_CONTROLKEY: if (ntokens >= 2) { keyid_t ckey; ckey = atol(tokens[1]); if (ckey == 0) { msyslog(LOG_ERR, "%s makes a poor control keyid", tokens[1]); } else { ctl_auth_keyid = ckey; } } break; case CONFIG_TRAP: if (ntokens < 2) { msyslog(LOG_ERR, "no address for trap command, line ignored"); break; } istart = 1; memset((char *)&peeraddr, 0, sizeof(peeraddr)); peeraddr.ss_family = default_ai_family; switch (matchkey(tokens[istart], addr_type, 0)) { case CONF_ADDR_IPV4: peeraddr.ss_family = AF_INET; istart++; break; case CONF_ADDR_IPV6: peeraddr.ss_family = AF_INET6; istart++; break; } if (getnetnum(tokens[istart], &peeraddr, 1, t_UNK) != 1) break; /* * Use peerversion for port number. Barf. */ errflg = 0; peerversion = 0; localaddr = 0; istart++; for (i = istart; i < ntokens-1; i++) switch (matchkey(tokens[i], trap_keywords, 1)) { case CONF_TRAP_PORT: if (i >= ntokens-1) { msyslog(LOG_ERR, "trap port requires an argument"); errflg = 1; break; } peerversion = atoi(tokens[++i]); if (peerversion <= 0 || peerversion > 32767) { msyslog(LOG_ERR, "invalid port number %s, trap ignored", tokens[i]); errflg = 1; } break; case CONF_TRAP_INTERFACE: if (i >= ntokens-1) { msyslog(LOG_ERR, "trap interface requires an argument"); errflg = 1; break; } memset((char *)&maskaddr, 0, sizeof(maskaddr)); maskaddr.ss_family = peeraddr.ss_family; if (getnetnum(tokens[++i], &maskaddr, 1, t_UNK) != 1) { errflg = 1; break; } localaddr = findinterface(&maskaddr); if (localaddr == NULL) { msyslog(LOG_ERR, "can't find interface with address %s", stoa(&maskaddr)); errflg = 1; } break; case CONFIG_UNKNOWN: errflg++; break; } if (!errflg) { if (peerversion != 0) ((struct sockaddr_in6*)&peeraddr)->sin6_port = htons( (u_short) peerversion); else ((struct sockaddr_in6*)&peeraddr)->sin6_port = htons(TRAPPORT); if (localaddr == NULL) localaddr = ANY_INTERFACE_CHOOSE(&peeraddr); if (!ctlsettrap(&peeraddr, localaddr, 0, NTP_VERSION)) msyslog(LOG_ERR, "can't set trap for %s, no resources", stoa(&peeraddr)); } break; case CONFIG_FUDGE: if (ntokens < 2) { msyslog(LOG_ERR, "no address for fudge command, line ignored"); break; } memset((char *)&peeraddr, 0, sizeof(peeraddr)); if (getnetnum(tokens[1], &peeraddr, 1, t_REF) != 1) break; if (!ISREFCLOCKADR(&peeraddr)) { msyslog(LOG_ERR, "%s is inappropriate address for the fudge command, line ignored", stoa(&peeraddr)); break; } memset((void *)&clock_stat, 0, sizeof clock_stat); fudgeflag = 0; errflg = 0; for (i = 2; i < ntokens-1; i++) { switch (c = matchkey(tokens[i], fudge_keywords, 1)) { case CONF_FDG_TIME1: if (sscanf(tokens[++i], "%lf", &clock_stat.fudgetime1) != 1) { msyslog(LOG_ERR, "fudge %s time1 value in error", stoa(&peeraddr)); errflg = i; break; } clock_stat.haveflags |= CLK_HAVETIME1; break; case CONF_FDG_TIME2: if (sscanf(tokens[++i], "%lf", &clock_stat.fudgetime2) != 1) { msyslog(LOG_ERR, "fudge %s time2 value in error", stoa(&peeraddr)); errflg = i; break; } clock_stat.haveflags |= CLK_HAVETIME2; break; case CONF_FDG_STRATUM: if (!atoint(tokens[++i], &stratum)) { msyslog(LOG_ERR, "fudge %s stratum value in error", stoa(&peeraddr)); errflg = i; break; } clock_stat.fudgeval1 = stratum; clock_stat.haveflags |= CLK_HAVEVAL1; break; case CONF_FDG_REFID: i++; memcpy(&clock_stat.fudgeval2, tokens[i], min(strlen(tokens[i]), 4)); clock_stat.haveflags |= CLK_HAVEVAL2; break; case CONF_FDG_FLAG1: case CONF_FDG_FLAG2: case CONF_FDG_FLAG3: case CONF_FDG_FLAG4: if (!atouint(tokens[++i], &fudgeflag) || fudgeflag > 1) { msyslog(LOG_ERR, "fudge %s flag value in error", stoa(&peeraddr)); errflg = i; break; } switch(c) { case CONF_FDG_FLAG1: c = CLK_FLAG1; clock_stat.haveflags|=CLK_HAVEFLAG1; break; case CONF_FDG_FLAG2: c = CLK_FLAG2; clock_stat.haveflags|=CLK_HAVEFLAG2; break; case CONF_FDG_FLAG3: c = CLK_FLAG3; clock_stat.haveflags|=CLK_HAVEFLAG3; break; case CONF_FDG_FLAG4: c = CLK_FLAG4; clock_stat.haveflags|=CLK_HAVEFLAG4; break; } if (fudgeflag == 0) clock_stat.flags &= ~c; else clock_stat.flags |= c; break; case CONFIG_UNKNOWN: errflg = -1; break; } } #ifdef REFCLOCK /* * If reference clock support isn't defined the * fudge line will still be accepted and syntax * checked, but will essentially do nothing. */ if (!errflg) { refclock_control(&peeraddr, &clock_stat, (struct refclockstat *)0); } #endif break; case CONFIG_STATSDIR: if (ntokens >= 2) stats_config(STATS_STATSDIR,tokens[1]); break; case CONFIG_STATISTICS: for (i = 1; i < ntokens; i++) { filegen = filegen_get(tokens[i]); if (filegen == NULL) { msyslog(LOG_ERR, "no statistics named %s available", tokens[i]); continue; } #ifdef DEBUG if (debug > 3) printf("enabling filegen for %s statistics \"%s%s\"\n", tokens[i], filegen->prefix, filegen->basename); #endif filegen->flag |= FGEN_FLAG_ENABLED; } break; case CONFIG_FILEGEN: if (ntokens < 2) { msyslog(LOG_ERR, "no id for filegen command, line ignored"); break; } filegen = filegen_get(tokens[1]); if (filegen == NULL) { msyslog(LOG_ERR, "unknown filegen \"%s\" ignored", tokens[1]); break; } /* * peerversion is (ab)used for filegen file (index) * peerkey is (ab)used for filegen type * peerflags is (ab)used for filegen flags */ peerversion = 0; peerkey = filegen->type; peerflags = filegen->flag; errflg = 0; for (i = 2; i < ntokens; i++) { switch (matchkey(tokens[i], filegen_keywords, 1)) { case CONF_FGEN_FILE: if (i >= ntokens - 1) { msyslog(LOG_ERR, "filegen %s file requires argument", tokens[1]); errflg = i; break; } peerversion = ++i; break; case CONF_FGEN_TYPE: if (i >= ntokens -1) { msyslog(LOG_ERR, "filegen %s type requires argument", tokens[1]); errflg = i; break; } peerkey = matchkey(tokens[++i], fgen_types, 1); if (peerkey == CONFIG_UNKNOWN) { msyslog(LOG_ERR, "filegen %s unknown type \"%s\"", tokens[1], tokens[i]); errflg = i; break; } break; case CONF_FGEN_FLAG_LINK: peerflags |= FGEN_FLAG_LINK; break; case CONF_FGEN_FLAG_NOLINK: peerflags &= ~FGEN_FLAG_LINK; break; case CONF_FGEN_FLAG_ENABLE: peerflags |= FGEN_FLAG_ENABLED; break; case CONF_FGEN_FLAG_DISABLE: peerflags &= ~FGEN_FLAG_ENABLED; break; } } if (!errflg) filegen_config(filegen, tokens[peerversion], (u_char)peerkey, (u_char)peerflags); break; case CONFIG_SETVAR: if (ntokens < 2) { msyslog(LOG_ERR, "no value for setvar command - line ignored"); } else { set_sys_var(tokens[1], strlen(tokens[1])+1, (u_short) (RW | ((((ntokens > 2) && !strcmp(tokens[2], "default"))) ? DEF : 0))); } break; case CONFIG_ENABLE: for (i = 1; i < ntokens; i++) { int flag; flag = matchkey(tokens[i], flags_keywords, 1); if (flag == CONFIG_UNKNOWN) { msyslog(LOG_ERR, "enable unknown flag %s", tokens[i]); errflg = 1; break; } proto_config(flag, 1, 0., NULL); } break; case CONFIG_DISABLE: for (i = 1; i < ntokens; i++) { int flag; flag = matchkey(tokens[i], flags_keywords, 1); if (flag == CONFIG_UNKNOWN) { msyslog(LOG_ERR, "disable unknown flag %s", tokens[i]); errflg = 1; break; } proto_config(flag, 0, 0., NULL); } break; case CONFIG_PHONE: for (i = 1; i < ntokens && i < MAXPHONE - 1; i++) { sys_phone[i - 1] = emalloc(strlen(tokens[i]) + 1); strcpy(sys_phone[i - 1], tokens[i]); } sys_phone[i] = NULL; break; case CONFIG_ADJ: { double ftemp; sscanf(tokens[1], "%lf", &ftemp); proto_config(PROTO_ADJ, 0, ftemp, NULL); } break; } } if (fp[0]) (void)fclose(fp[0]); #ifdef HAVE_NETINFO if (config_netinfo) free_netinfo_config(config_netinfo); #endif /* HAVE_NETINFO */ #if !defined(VMS) && !defined(SYS_VXWORKS) /* find a keyid */ if (info_auth_keyid == 0) req_keyid = 65535; else req_keyid = info_auth_keyid; /* if doesn't exist, make up one at random */ if (!authhavekey(req_keyid)) { char rankey[9]; int j; for (i = 0; i < 8; i++) for (j = 1; j < 100; ++j) { - rankey[i] = (char) (ntp_random() & 0xff); + rankey[i] = (char) (arc4random() & 0xff); if (rankey[i] != 0) break; } rankey[8] = 0; authusekey(req_keyid, KEY_TYPE_MD5, (u_char *)rankey); authtrust(req_keyid, 1); if (!authhavekey(req_keyid)) { msyslog(LOG_ERR, "getconfig: Couldn't generate a valid random key!"); /* HMS: Should this be fatal? */ } } /* save keyid so we will accept config requests with it */ info_auth_keyid = req_keyid; #endif /* !defined(VMS) && !defined(SYS_VXWORKS) */ if (res_fp != NULL) { if (call_resolver) { /* * Need name resolution */ do_resolve_internal(); } } } #ifdef HAVE_NETINFO /* * get_netinfo_config - find the nearest NetInfo domain with an ntp * configuration and initialize the configuration state. */ static struct netinfo_config_state * get_netinfo_config() { ni_status status; void *domain; ni_id config_dir; struct netinfo_config_state *config; if (ni_open(NULL, ".", &domain) != NI_OK) return NULL; while ((status = ni_pathsearch(domain, &config_dir, NETINFO_CONFIG_DIR)) == NI_NODIR) { void *next_domain; if (ni_open(domain, "..", &next_domain) != NI_OK) { ni_free(next_domain); break; } ni_free(domain); domain = next_domain; } if (status != NI_OK) { ni_free(domain); return NULL; } config = (struct netinfo_config_state *)malloc(sizeof(struct netinfo_config_state)); config->domain = domain; config->config_dir = config_dir; config->prop_index = 0; config->val_index = 0; config->val_list = NULL; return config; } /* * free_netinfo_config - release NetInfo configuration state */ static void free_netinfo_config(struct netinfo_config_state *config) { ni_free(config->domain); free(config); } /* * gettokens_netinfo - return tokens from NetInfo */ static int gettokens_netinfo ( struct netinfo_config_state *config, char **tokenlist, int *ntokens ) { int prop_index = config->prop_index; int val_index = config->val_index; char **val_list = config->val_list; /* * Iterate through each keyword and look for a property that matches it. */ again: if (!val_list) { for (; prop_index < (sizeof(keywords)/sizeof(keywords[0])); prop_index++) { ni_namelist namelist; struct keyword current_prop = keywords[prop_index]; /* * For each value associated in the property, we're going to return * a separate line. We squirrel away the values in the config state * so the next time through, we don't need to do this lookup. */ NI_INIT(&namelist); if (ni_lookupprop(config->domain, &config->config_dir, current_prop.text, &namelist) == NI_OK) { ni_index index; /* Found the property, but it has no values */ if (namelist.ni_namelist_len == 0) continue; if (! (val_list = config->val_list = (char**)malloc(sizeof(char*) * (namelist.ni_namelist_len + 1)))) { msyslog(LOG_ERR, "out of memory while configuring"); break; } for (index = 0; index < namelist.ni_namelist_len; index++) { char *value = namelist.ni_namelist_val[index]; if (! (val_list[index] = (char*)malloc(strlen(value)+1))) { msyslog(LOG_ERR, "out of memory while configuring"); break; } strcpy(val_list[index], value); } val_list[index] = NULL; break; } ni_namelist_free(&namelist); } config->prop_index = prop_index; } /* No list; we're done here. */ if (!val_list) return CONFIG_UNKNOWN; /* * We have a list of values for the current property. * Iterate through them and return each in order. */ if (val_list[val_index]) { int ntok = 1; int quoted = 0; char *tokens = val_list[val_index]; msyslog(LOG_INFO, "%s %s", keywords[prop_index].text, val_list[val_index]); (const char*)tokenlist[0] = keywords[prop_index].text; for (ntok = 1; ntok < MAXTOKENS; ntok++) { tokenlist[ntok] = tokens; while (!ISEOL(*tokens) && (!ISSPACE(*tokens) || quoted)) quoted ^= (*tokens++ == '"'); if (ISEOL(*tokens)) { *tokens = '\0'; break; } else { /* must be space */ *tokens++ = '\0'; while (ISSPACE(*tokens)) tokens++; if (ISEOL(*tokens)) break; } } if (ntok == MAXTOKENS) { /* HMS: chomp it to lose the EOL? */ msyslog(LOG_ERR, "gettokens_netinfo: too many tokens. Ignoring: %s", tokens); } else { *ntokens = ntok + 1; } config->val_index++; /* HMS: Should this be in the 'else'? */ return keywords[prop_index].keytype; } /* We're done with the current property. */ prop_index = ++config->prop_index; /* Free val_list and reset counters. */ for (val_index = 0; val_list[val_index]; val_index++) free(val_list[val_index]); free(val_list); val_list = config->val_list = NULL; val_index = config->val_index = 0; goto again; } #endif /* HAVE_NETINFO */ /* * gettokens - read a line and return tokens */ static int gettokens ( FILE *fp, char *line, char **tokenlist, int *ntokens ) { register char *cp; register int ntok; register int quoted = 0; /* * Find start of first token */ again: while ((cp = fgets(line, MAXLINE, fp)) != NULL) { cp = line; while (ISSPACE(*cp)) cp++; if (!ISEOL(*cp)) break; } if (cp == NULL) { *ntokens = 0; return CONFIG_UNKNOWN; /* hack. Is recognized as EOF */ } /* * Now separate out the tokens */ for (ntok = 0; ntok < MAXTOKENS; ntok++) { tokenlist[ntok] = cp; while (!ISEOL(*cp) && (!ISSPACE(*cp) || quoted)) quoted ^= (*cp++ == '"'); if (ISEOL(*cp)) { *cp = '\0'; break; } else { /* must be space */ *cp++ = '\0'; while (ISSPACE(*cp)) cp++; if (ISEOL(*cp)) break; } } /* Heiko: Remove leading and trailing quotes around tokens */ { int i,j = 0; for (i = 0; i < ntok; i++) { /* Now check if the first char is a quote and remove that */ if ( tokenlist[ntok][0] == '"' ) tokenlist[ntok]++; /* Now check the last char ... */ j = strlen(tokenlist[ntok])-1; if ( tokenlist[ntok][j] == '"' ) tokenlist[ntok][j] = '\0'; } } if (ntok == MAXTOKENS) { --ntok; /* HMS: chomp it to lose the EOL? */ msyslog(LOG_ERR, "gettokens: too many tokens on the line. Ignoring %s", cp); } else { /* * Return the match */ *ntokens = ntok + 1; ntok = matchkey(tokenlist[0], keywords, 1); if (ntok == CONFIG_UNKNOWN) goto again; } return ntok; } /* * matchkey - match a keyword to a list */ static int matchkey( register char *word, register struct keyword *keys, int complain ) { for (;;) { if (keys->keytype == CONFIG_UNKNOWN) { if (complain) msyslog(LOG_ERR, "configure: keyword \"%s\" unknown, line ignored", word); return CONFIG_UNKNOWN; } if (STRSAME(word, keys->text)) return keys->keytype; keys++; } } /* * getnetnum - return a net number (this is crude, but careful) */ static int getnetnum( const char *num, struct sockaddr_storage *addr, int complain, enum gnn_type a_type ) { struct addrinfo hints; struct addrinfo *ptr; int retval; #if 0 printf("getnetnum: <%s> is a %s (%d)\n", num, (a_type == t_UNK) ? "t_UNK" : (a_type == t_REF) ? "t_REF" : (a_type == t_MSK) ? "t_MSK" : "???", a_type); #endif /* Get host address. Looking for UDP datagram connection */ memset(&hints, 0, sizeof (hints)); if (addr->ss_family == AF_INET || addr->ss_family == AF_INET6) hints.ai_family = addr->ss_family; else hints.ai_family = AF_UNSPEC; /* * If we don't have an IPv6 stack, just look up IPv4 addresses */ if (isc_net_probeipv6() != ISC_R_SUCCESS) hints.ai_family = AF_INET; hints.ai_socktype = SOCK_DGRAM; if (a_type != t_UNK) { hints.ai_flags = AI_NUMERICHOST; } #ifdef DEBUG if (debug > 3) printf("getnetnum: calling getaddrinfo(%s,...)\n", num); #endif retval = getaddrinfo(num, "ntp", &hints, &ptr); if (retval != 0 || (ptr->ai_family == AF_INET6 && isc_net_probeipv6() != ISC_R_SUCCESS)) { if (complain) msyslog(LOG_ERR, "getaddrinfo: \"%s\" invalid host address, ignored", num); #ifdef DEBUG if (debug > 0) printf( "getaddrinfo: \"%s\" invalid host address%s.\n", num, (complain) ? ", ignored" : ""); #endif if (retval == 0 && ptr->ai_family == AF_INET6 && isc_net_probeipv6() != ISC_R_SUCCESS) { return -1; } else { return 0; } } memcpy(addr, ptr->ai_addr, ptr->ai_addrlen); #ifdef DEBUG if (debug > 1) printf("getnetnum given %s, got %s (%s/%d)\n", num, stoa(addr), (a_type == t_UNK) ? "t_UNK" : (a_type == t_REF) ? "t_REF" : (a_type == t_MSK) ? "t_MSK" : "???", a_type); #endif freeaddrinfo(ptr); return 1; } #if !defined(VMS) && !defined(SYS_WINNT) /* * catchchild - receive the resolver's exit status */ static RETSIGTYPE catchchild( int sig ) { /* * We only start up one child, and if we're here * it should have already exited. Hence the following * shouldn't hang. If it does, please tell me. */ #if !defined (SYS_WINNT) && !defined(SYS_VXWORKS) (void) wait(0); #endif /* SYS_WINNT && VXWORKS*/ } #endif /* VMS */ /* * save_resolve - save configuration info into a file for later name resolution */ static void save_resolve( char *name, int mode, int version, int minpoll, int maxpoll, u_int flags, int ttl, keyid_t keyid, u_char *keystr, u_char peeraf ) { #ifndef SYS_VXWORKS if (res_fp == NULL) { #ifndef SYS_WINNT (void) strcpy(res_file, RES_TEMPFILE); #else /* no /tmp directory under NT */ { if(!(GetTempPath((DWORD)MAX_PATH, (LPTSTR)res_file))) { msyslog(LOG_ERR, "cannot get pathname for temporary directory: %m"); return; } (void) strcat(res_file, "ntpdXXXXXX"); } #endif /* SYS_WINNT */ #ifdef HAVE_MKSTEMP { int fd; res_fp = NULL; if ((fd = mkstemp(res_file)) != -1) res_fp = fdopen(fd, "r+"); } #else (void) mktemp(res_file); res_fp = fopen(res_file, "w"); #endif if (res_fp == NULL) { msyslog(LOG_ERR, "open failed for %s: %m", res_file); return; } } #ifdef DEBUG if (debug) { printf("resolving %s\n", name); } #endif (void)fprintf(res_fp, "%s %u %d %d %d %d %d %d %u %s\n", name, peeraf, mode, version, minpoll, maxpoll, flags, ttl, keyid, keystr); #ifdef DEBUG if (debug > 1) printf("config: %s %u %d %d %d %d %x %d %u %s\n", name, peeraf, mode, version, minpoll, maxpoll, flags, ttl, keyid, keystr); #endif #else /* SYS_VXWORKS */ /* save resolve info to a struct */ #endif /* SYS_VXWORKS */ } /* * abort_resolve - terminate the resolver stuff and delete the file */ static void abort_resolve(void) { /* * In an ideal world we would might reread the file and * log the hosts which aren't getting configured. Since * this is too much work, however, just close and delete * the temp file. */ if (res_fp != NULL) (void) fclose(res_fp); res_fp = NULL; #ifndef SYS_VXWORKS /* we don't open the file to begin with */ #if !defined(VMS) (void) unlink(res_file); #else (void) delete(res_file); #endif /* VMS */ #endif /* SYS_VXWORKS */ } /* * do_resolve_internal - start up the resolver function (not program) */ /* * On VMS, this routine will simply refuse to resolve anything. * * Possible implementation: keep `res_file' in memory, do async * name resolution via QIO, update from within completion AST. * I'm unlikely to find the time for doing this, though. -wjm */ static void do_resolve_internal(void) { int i; if (res_fp == NULL) { /* belch */ msyslog(LOG_ERR, "do_resolve_internal: Fatal: res_fp == NULL"); exit(1); } /* we are done with this now */ (void) fclose(res_fp); res_fp = NULL; #if !defined(VMS) && !defined (SYS_VXWORKS) req_file = res_file; /* set up pointer to res file */ #ifndef SYS_WINNT (void) signal_no_reset(SIGCHLD, catchchild); #ifndef SYS_VXWORKS /* the parent process will write to the pipe * in order to wake up to child process * which may be waiting in a select() call * on the read fd */ if (pipe(resolver_pipe_fd) < 0) { msyslog(LOG_ERR, "unable to open resolver pipe"); exit(1); } i = fork(); /* Shouldn't the code below be re-ordered? * I.e. first check if the fork() returned an error, then * check whether we're parent or child. * Martin Burnicki */ if (i == 0) { /* * this used to close everything * I don't think this is necessary */ /* * To the unknown commenter above: * Well, I think it's better to clean up * after oneself. I have had problems with * refclock-io when intres was running - things * where fine again when ntpintres was gone. * So some systems react erratic at least. * * Frank Kardel * * 94-11-16: * Further debugging has proven that the above is * absolutely harmful. The internal resolver * is still in the SIGIO process group and the lingering * async io information causes it to process requests from * all file decriptor causing a race between the NTP daemon * and the resolver. which then eats data when it wins 8-(. * It is absolutly necessary to kill any IO associations * shared with the NTP daemon. * * We also block SIGIO (currently no ports means to * disable the signal handle for IO). * * Thanks to wgstuken@informatik.uni-erlangen.de to notice * that it is the ntp-resolver child running into trouble. * * THUS: */ /* This is the child process who will read the pipe, * so we close the write fd */ close(resolver_pipe_fd[1]); closelog(); kill_asyncio(0); (void) signal_no_reset(SIGCHLD, SIG_DFL); #ifdef DEBUG if (0) debug = 2; #endif # ifndef LOG_DAEMON openlog("ntpd_initres", LOG_PID); # else /* LOG_DAEMON */ # ifndef LOG_NTP # define LOG_NTP LOG_DAEMON # endif openlog("ntpd_initres", LOG_PID | LOG_NDELAY, LOG_NTP); #ifndef SYS_CYGWIN32 # ifdef DEBUG if (debug) setlogmask(LOG_UPTO(LOG_DEBUG)); else # endif /* DEBUG */ setlogmask(LOG_UPTO(LOG_DEBUG)); /* @@@ was INFO */ # endif /* LOG_DAEMON */ #endif ntp_intres(); /* * If we got here, the intres code screwed up. * Print something so we don't die without complaint */ msyslog(LOG_ERR, "call to ntp_intres lost"); abort_resolve(); exit(1); } #else /* vxWorks spawns a thread... -casey */ i = sp (ntp_intres); /*i = taskSpawn("ntp_intres",100,VX_FP_TASK,20000,ntp_intres);*/ #endif if (i == -1) { msyslog(LOG_ERR, "fork() failed, can't start ntp_intres: %m"); (void) signal_no_reset(SIGCHLD, SIG_DFL); abort_resolve(); } else { /* This is the parent process who will write to the pipe, * so we close the read fd */ close(resolver_pipe_fd[0]); } #else /* SYS_WINNT */ { /* NT's equivalent of fork() is _spawn(), but the start point * of the new process is an executable filename rather than * a function name as desired here. */ DWORD dwThreadId; fflush(stdout); ResolverEventHandle = CreateEvent(NULL, FALSE, FALSE, NULL); if (ResolverEventHandle == NULL) { msyslog(LOG_ERR, "Unable to create resolver event object, can't start ntp_intres"); abort_resolve(); } ResolverThreadHandle = CreateThread( NULL, /* no security attributes */ 0, /* use default stack size */ (LPTHREAD_START_ROUTINE) ntp_intres, /* thread function */ NULL, /* argument to thread function */ 0, /* use default creation flags */ &dwThreadId); /* returns the thread identifier */ if (ResolverThreadHandle == NULL) { msyslog(LOG_ERR, "CreateThread() failed, can't start ntp_intres"); CloseHandle(ResolverEventHandle); ResolverEventHandle = NULL; abort_resolve(); } } #endif /* SYS_WINNT */ #else /* VMS VX_WORKS */ msyslog(LOG_ERR, "Name resolution not implemented for VMS - use numeric addresses"); abort_resolve(); #endif /* VMS VX_WORKS */ } Index: head/contrib/ntp/ntpd/ntp_control.c =================================================================== --- head/contrib/ntp/ntpd/ntp_control.c (revision 276070) +++ head/contrib/ntp/ntpd/ntp_control.c (revision 276071) @@ -1,3001 +1,3016 @@ /* * ntp_control.c - respond to control messages and send async traps */ /* * $FreeBSD$ */ #ifdef HAVE_CONFIG_H #include #endif #include "ntpd.h" #include "ntp_io.h" #include "ntp_refclock.h" #include "ntp_control.h" #include "ntp_unixtime.h" #include "ntp_stdlib.h" #include #include #include #include #include +#ifndef MIN +#define MIN(a, b) (((a) <= (b)) ? (a) : (b)) +#endif + /* * Structure to hold request procedure information */ #define NOAUTH 0 #define AUTH 1 #define NO_REQUEST (-1) struct ctl_proc { short control_code; /* defined request code */ u_short flags; /* flags word */ void (*handler) P((struct recvbuf *, int)); /* handle request */ }; /* * Only one flag. Authentication required or not. */ #define NOAUTH 0 #define AUTH 1 /* * Request processing routines */ static void ctl_error P((int)); #ifdef REFCLOCK static u_short ctlclkstatus P((struct refclockstat *)); #endif static void ctl_flushpkt P((int)); static void ctl_putdata P((const char *, unsigned int, int)); static void ctl_putstr P((const char *, const char *, unsigned int)); static void ctl_putdbl P((const char *, double)); static void ctl_putuint P((const char *, u_long)); static void ctl_puthex P((const char *, u_long)); static void ctl_putint P((const char *, long)); static void ctl_putts P((const char *, l_fp *)); static void ctl_putadr P((const char *, u_int32, struct sockaddr_storage*)); static void ctl_putid P((const char *, char *)); static void ctl_putarray P((const char *, double *, int)); static void ctl_putsys P((int)); static void ctl_putpeer P((int, struct peer *)); #ifdef OPENSSL static void ctl_putfs P((const char *, tstamp_t)); #endif #ifdef REFCLOCK static void ctl_putclock P((int, struct refclockstat *, int)); #endif /* REFCLOCK */ static struct ctl_var *ctl_getitem P((struct ctl_var *, char **)); static u_long count_var P((struct ctl_var *)); static void control_unspec P((struct recvbuf *, int)); static void read_status P((struct recvbuf *, int)); static void read_variables P((struct recvbuf *, int)); static void write_variables P((struct recvbuf *, int)); static void read_clock_status P((struct recvbuf *, int)); static void write_clock_status P((struct recvbuf *, int)); static void set_trap P((struct recvbuf *, int)); static void unset_trap P((struct recvbuf *, int)); static struct ctl_trap *ctlfindtrap P((struct sockaddr_storage *, struct interface *)); static struct ctl_proc control_codes[] = { { CTL_OP_UNSPEC, NOAUTH, control_unspec }, { CTL_OP_READSTAT, NOAUTH, read_status }, { CTL_OP_READVAR, NOAUTH, read_variables }, { CTL_OP_WRITEVAR, AUTH, write_variables }, { CTL_OP_READCLOCK, NOAUTH, read_clock_status }, { CTL_OP_WRITECLOCK, NOAUTH, write_clock_status }, { CTL_OP_SETTRAP, NOAUTH, set_trap }, { CTL_OP_UNSETTRAP, NOAUTH, unset_trap }, { NO_REQUEST, 0 } }; /* * System variable values. The array can be indexed by the variable * index to find the textual name. */ static struct ctl_var sys_var[] = { { 0, PADDING, "" }, /* 0 */ { CS_LEAP, RW, "leap" }, /* 1 */ { CS_STRATUM, RO, "stratum" }, /* 2 */ { CS_PRECISION, RO, "precision" }, /* 3 */ { CS_ROOTDELAY, RO, "rootdelay" }, /* 4 */ { CS_ROOTDISPERSION, RO, "rootdispersion" }, /* 5 */ { CS_REFID, RO, "refid" }, /* 6 */ { CS_REFTIME, RO, "reftime" }, /* 7 */ { CS_POLL, RO, "poll" }, /* 8 */ { CS_PEERID, RO, "peer" }, /* 9 */ { CS_STATE, RO, "state" }, /* 10 */ { CS_OFFSET, RO, "offset" }, /* 11 */ { CS_DRIFT, RO, "frequency" }, /* 12 */ { CS_JITTER, RO, "jitter" }, /* 13 */ { CS_ERROR, RO, "noise" }, /* 14 */ { CS_CLOCK, RO, "clock" }, /* 15 */ { CS_PROCESSOR, RO, "processor" }, /* 16 */ { CS_SYSTEM, RO, "system" }, /* 17 */ { CS_VERSION, RO, "version" }, /* 18 */ { CS_STABIL, RO, "stability" }, /* 19 */ { CS_VARLIST, RO, "sys_var_list" }, /* 20 */ #ifdef OPENSSL { CS_FLAGS, RO, "flags" }, /* 21 */ { CS_HOST, RO, "hostname" }, /* 22 */ { CS_PUBLIC, RO, "update" }, /* 23 */ { CS_CERTIF, RO, "cert" }, /* 24 */ { CS_REVTIME, RO, "expire" }, /* 25 */ { CS_LEAPTAB, RO, "leapsec" }, /* 26 */ { CS_TAI, RO, "tai" }, /* 27 */ { CS_DIGEST, RO, "signature" }, /* 28 */ { CS_IDENT, RO, "ident" }, /* 29 */ { CS_REVOKE, RO, "expire" }, /* 30 */ #endif /* OPENSSL */ { 0, EOV, "" } /* 21/31 */ }; static struct ctl_var *ext_sys_var = (struct ctl_var *)0; /* * System variables we print by default (in fuzzball order, * more-or-less) */ static u_char def_sys_var[] = { CS_VERSION, CS_PROCESSOR, CS_SYSTEM, CS_LEAP, CS_STRATUM, CS_PRECISION, CS_ROOTDELAY, CS_ROOTDISPERSION, CS_PEERID, CS_REFID, CS_REFTIME, CS_POLL, CS_CLOCK, CS_STATE, CS_OFFSET, CS_DRIFT, CS_JITTER, CS_ERROR, CS_STABIL, #ifdef OPENSSL CS_HOST, CS_DIGEST, CS_FLAGS, CS_PUBLIC, CS_IDENT, CS_LEAPTAB, CS_TAI, CS_CERTIF, #endif /* OPENSSL */ 0 }; /* * Peer variable list */ static struct ctl_var peer_var[] = { { 0, PADDING, "" }, /* 0 */ { CP_CONFIG, RO, "config" }, /* 1 */ { CP_AUTHENABLE, RO, "authenable" }, /* 2 */ { CP_AUTHENTIC, RO, "authentic" }, /* 3 */ { CP_SRCADR, RO, "srcadr" }, /* 4 */ { CP_SRCPORT, RO, "srcport" }, /* 5 */ { CP_DSTADR, RO, "dstadr" }, /* 6 */ { CP_DSTPORT, RO, "dstport" }, /* 7 */ { CP_LEAP, RO, "leap" }, /* 8 */ { CP_HMODE, RO, "hmode" }, /* 9 */ { CP_STRATUM, RO, "stratum" }, /* 10 */ { CP_PPOLL, RO, "ppoll" }, /* 11 */ { CP_HPOLL, RO, "hpoll" }, /* 12 */ { CP_PRECISION, RO, "precision" }, /* 13 */ { CP_ROOTDELAY, RO, "rootdelay" }, /* 14 */ { CP_ROOTDISPERSION, RO, "rootdispersion" }, /* 15 */ { CP_REFID, RO, "refid" }, /* 16 */ { CP_REFTIME, RO, "reftime" }, /* 17 */ { CP_ORG, RO, "org" }, /* 18 */ { CP_REC, RO, "rec" }, /* 19 */ { CP_XMT, RO, "xmt" }, /* 20 */ { CP_REACH, RO, "reach" }, /* 21 */ { CP_UNREACH, RO, "unreach" }, /* 22 */ { CP_TIMER, RO, "timer" }, /* 23 */ { CP_DELAY, RO, "delay" }, /* 24 */ { CP_OFFSET, RO, "offset" }, /* 25 */ { CP_JITTER, RO, "jitter" }, /* 26 */ { CP_DISPERSION, RO, "dispersion" }, /* 27 */ { CP_KEYID, RO, "keyid" }, /* 28 */ { CP_FILTDELAY, RO, "filtdelay=" }, /* 29 */ { CP_FILTOFFSET, RO, "filtoffset=" }, /* 30 */ { CP_PMODE, RO, "pmode" }, /* 31 */ { CP_RECEIVED, RO, "received"}, /* 32 */ { CP_SENT, RO, "sent" }, /* 33 */ { CP_FILTERROR, RO, "filtdisp=" }, /* 34 */ { CP_FLASH, RO, "flash" }, /* 35 */ { CP_TTL, RO, "ttl" }, /* 36 */ { CP_VARLIST, RO, "peer_var_list" }, /* 37 */ #ifdef OPENSSL { CP_FLAGS, RO, "flags" }, /* 38 */ { CP_HOST, RO, "hostname" }, /* 39 */ { CP_VALID, RO, "valid" }, /* 40 */ { CP_INITSEQ, RO, "initsequence" }, /* 41 */ { CP_INITKEY, RO, "initkey" }, /* 42 */ { CP_INITTSP, RO, "timestamp" }, /* 43 */ { CP_DIGEST, RO, "signature" }, /* 44 */ { CP_IDENT, RO, "trust" }, /* 45 */ #endif /* OPENSSL */ { 0, EOV, "" } /* 38/46 */ }; /* * Peer variables we print by default */ static u_char def_peer_var[] = { CP_SRCADR, CP_SRCPORT, CP_DSTADR, CP_DSTPORT, CP_LEAP, CP_STRATUM, CP_PRECISION, CP_ROOTDELAY, CP_ROOTDISPERSION, CP_REFID, CP_REACH, CP_UNREACH, CP_HMODE, CP_PMODE, CP_HPOLL, CP_PPOLL, CP_FLASH, CP_KEYID, CP_TTL, CP_OFFSET, CP_DELAY, CP_DISPERSION, CP_JITTER, CP_REFTIME, CP_ORG, CP_REC, CP_XMT, CP_FILTDELAY, CP_FILTOFFSET, CP_FILTERROR, #ifdef OPENSSL CP_HOST, CP_DIGEST, CP_VALID, CP_FLAGS, CP_IDENT, CP_INITSEQ, #endif /* OPENSSL */ 0 }; #ifdef REFCLOCK /* * Clock variable list */ static struct ctl_var clock_var[] = { { 0, PADDING, "" }, /* 0 */ { CC_TYPE, RO, "type" }, /* 1 */ { CC_TIMECODE, RO, "timecode" }, /* 2 */ { CC_POLL, RO, "poll" }, /* 3 */ { CC_NOREPLY, RO, "noreply" }, /* 4 */ { CC_BADFORMAT, RO, "badformat" }, /* 5 */ { CC_BADDATA, RO, "baddata" }, /* 6 */ { CC_FUDGETIME1, RO, "fudgetime1" }, /* 7 */ { CC_FUDGETIME2, RO, "fudgetime2" }, /* 8 */ { CC_FUDGEVAL1, RO, "stratum" }, /* 9 */ { CC_FUDGEVAL2, RO, "refid" }, /* 10 */ { CC_FLAGS, RO, "flags" }, /* 11 */ { CC_DEVICE, RO, "device" }, /* 12 */ { CC_VARLIST, RO, "clock_var_list" }, /* 13 */ { 0, EOV, "" } /* 14 */ }; /* * Clock variables printed by default */ static u_char def_clock_var[] = { CC_DEVICE, CC_TYPE, /* won't be output if device = known */ CC_TIMECODE, CC_POLL, CC_NOREPLY, CC_BADFORMAT, CC_BADDATA, CC_FUDGETIME1, CC_FUDGETIME2, CC_FUDGEVAL1, CC_FUDGEVAL2, CC_FLAGS, 0 }; #endif /* * System and processor definitions. */ #ifndef HAVE_UNAME # ifndef STR_SYSTEM # define STR_SYSTEM "UNIX" # endif # ifndef STR_PROCESSOR # define STR_PROCESSOR "unknown" # endif static char str_system[] = STR_SYSTEM; static char str_processor[] = STR_PROCESSOR; #else # include static struct utsname utsnamebuf; #endif /* HAVE_UNAME */ /* * Trap structures. We only allow a few of these, and send a copy of * each async message to each live one. Traps time out after an hour, it * is up to the trap receipient to keep resetting it to avoid being * timed out. */ /* ntp_request.c */ struct ctl_trap ctl_trap[CTL_MAXTRAPS]; int num_ctl_traps; /* * Type bits, for ctlsettrap() call. */ #define TRAP_TYPE_CONFIG 0 /* used by configuration code */ #define TRAP_TYPE_PRIO 1 /* priority trap */ #define TRAP_TYPE_NONPRIO 2 /* nonpriority trap */ /* * List relating reference clock types to control message time sources. * Index by the reference clock type. This list will only be used iff * the reference clock driver doesn't set peer->sstclktype to something * different than CTL_SST_TS_UNSPEC. */ static u_char clocktypes[] = { CTL_SST_TS_NTP, /* REFCLK_NONE (0) */ CTL_SST_TS_LOCAL, /* REFCLK_LOCALCLOCK (1) */ CTL_SST_TS_UHF, /* deprecated REFCLK_GPS_TRAK (2) */ CTL_SST_TS_HF, /* REFCLK_WWV_PST (3) */ CTL_SST_TS_LF, /* REFCLK_WWVB_SPECTRACOM (4) */ CTL_SST_TS_UHF, /* REFCLK_TRUETIME (5) */ CTL_SST_TS_UHF, /* REFCLK_GOES_TRAK (6) IRIG_AUDIO? */ CTL_SST_TS_HF, /* REFCLK_CHU (7) */ CTL_SST_TS_LF, /* REFCLOCK_PARSE (default) (8) */ CTL_SST_TS_LF, /* REFCLK_GPS_MX4200 (9) */ CTL_SST_TS_UHF, /* REFCLK_GPS_AS2201 (10) */ CTL_SST_TS_UHF, /* REFCLK_GPS_ARBITER (11) */ CTL_SST_TS_UHF, /* REFCLK_IRIG_TPRO (12) */ CTL_SST_TS_ATOM, /* REFCLK_ATOM_LEITCH (13) */ CTL_SST_TS_LF, /* deprecated REFCLK_MSF_EES (14) */ CTL_SST_TS_NTP, /* not used (15) */ CTL_SST_TS_UHF, /* REFCLK_IRIG_BANCOMM (16) */ CTL_SST_TS_UHF, /* REFCLK_GPS_DATU (17) */ CTL_SST_TS_TELEPHONE, /* REFCLK_NIST_ACTS (18) */ CTL_SST_TS_HF, /* REFCLK_WWV_HEATH (19) */ CTL_SST_TS_UHF, /* REFCLK_GPS_NMEA (20) */ CTL_SST_TS_UHF, /* REFCLK_GPS_VME (21) */ CTL_SST_TS_ATOM, /* REFCLK_ATOM_PPS (22) */ CTL_SST_TS_NTP, /* not used (23) */ CTL_SST_TS_NTP, /* not used (24) */ CTL_SST_TS_NTP, /* not used (25) */ CTL_SST_TS_UHF, /* REFCLK_GPS_HP (26) */ CTL_SST_TS_TELEPHONE, /* REFCLK_ARCRON_MSF (27) */ CTL_SST_TS_TELEPHONE, /* REFCLK_SHM (28) */ CTL_SST_TS_UHF, /* REFCLK_PALISADE (29) */ CTL_SST_TS_UHF, /* REFCLK_ONCORE (30) */ CTL_SST_TS_UHF, /* REFCLK_JUPITER (31) */ CTL_SST_TS_LF, /* REFCLK_CHRONOLOG (32) */ CTL_SST_TS_LF, /* REFCLK_DUMBCLOCK (33) */ CTL_SST_TS_LF, /* REFCLK_ULINK (34) */ CTL_SST_TS_LF, /* REFCLK_PCF (35) */ CTL_SST_TS_LF, /* REFCLK_WWV (36) */ CTL_SST_TS_LF, /* REFCLK_FG (37) */ CTL_SST_TS_UHF, /* REFCLK_HOPF_SERIAL (38) */ CTL_SST_TS_UHF, /* REFCLK_HOPF_PCI (39) */ CTL_SST_TS_LF, /* REFCLK_JJY (40) */ CTL_SST_TS_UHF, /* REFCLK_TT560 (41) */ CTL_SST_TS_UHF, /* REFCLK_ZYFER (42) */ CTL_SST_TS_UHF, /* REFCLK_RIPENCC (43) */ CTL_SST_TS_UHF, /* REFCLK_NEOCLOCK4X (44) */ }; /* * Keyid used for authenticating write requests. */ keyid_t ctl_auth_keyid; /* * We keep track of the last error reported by the system internally */ static u_char ctl_sys_last_event; static u_char ctl_sys_num_events; /* * Statistic counters to keep track of requests and responses. */ u_long ctltimereset; /* time stats reset */ u_long numctlreq; /* number of requests we've received */ u_long numctlbadpkts; /* number of bad control packets */ u_long numctlresponses; /* number of resp packets sent with data */ u_long numctlfrags; /* number of fragments sent */ u_long numctlerrors; /* number of error responses sent */ u_long numctltooshort; /* number of too short input packets */ u_long numctlinputresp; /* number of responses on input */ u_long numctlinputfrag; /* number of fragments on input */ u_long numctlinputerr; /* number of input pkts with err bit set */ u_long numctlbadoffset; /* number of input pkts with nonzero offset */ u_long numctlbadversion; /* number of input pkts with unknown version */ u_long numctldatatooshort; /* data too short for count */ u_long numctlbadop; /* bad op code found in packet */ u_long numasyncmsgs; /* number of async messages we've sent */ /* * Response packet used by these routines. Also some state information * so that we can handle packet formatting within a common set of * subroutines. Note we try to enter data in place whenever possible, * but the need to set the more bit correctly means we occasionally * use the extra buffer and copy. */ static struct ntp_control rpkt; static u_char res_version; static u_char res_opcode; static associd_t res_associd; static int res_offset; static u_char * datapt; static u_char * dataend; static int datalinelen; static int datanotbinflag; static struct sockaddr_storage *rmt_addr; static struct interface *lcl_inter; static u_char res_authenticate; static u_char res_authokay; static keyid_t res_keyid; #define MAXDATALINELEN (72) static u_char res_async; /* set to 1 if this is async trap response */ /* * Pointers for saving state when decoding request packets */ static char *reqpt; static char *reqend; /* * init_control - initialize request data */ void init_control(void) { int i; #ifdef HAVE_UNAME uname(&utsnamebuf); #endif /* HAVE_UNAME */ ctl_clr_stats(); ctl_auth_keyid = 0; ctl_sys_last_event = EVNT_UNSPEC; ctl_sys_num_events = 0; num_ctl_traps = 0; for (i = 0; i < CTL_MAXTRAPS; i++) ctl_trap[i].tr_flags = 0; } /* * ctl_error - send an error response for the current request */ static void ctl_error( int errcode ) { #ifdef DEBUG if (debug >= 4) printf("sending control error %d\n", errcode); #endif /* * Fill in the fields. We assume rpkt.sequence and rpkt.associd * have already been filled in. */ rpkt.r_m_e_op = (u_char) (CTL_RESPONSE|CTL_ERROR|(res_opcode & CTL_OP_MASK)); rpkt.status = htons((u_short) ((errcode<<8) & 0xff00)); rpkt.count = 0; /* * send packet and bump counters */ if (res_authenticate && sys_authenticate) { int maclen; *(u_int32 *)((u_char *)&rpkt + CTL_HEADER_LEN) = htonl(res_keyid); maclen = authencrypt(res_keyid, (u_int32 *)&rpkt, CTL_HEADER_LEN); sendpkt(rmt_addr, lcl_inter, -2, (struct pkt *)&rpkt, CTL_HEADER_LEN + maclen); } else { sendpkt(rmt_addr, lcl_inter, -3, (struct pkt *)&rpkt, CTL_HEADER_LEN); } numctlerrors++; } /* * process_control - process an incoming control message */ void process_control( struct recvbuf *rbufp, int restrict_mask ) { register struct ntp_control *pkt; register int req_count; register int req_data; register struct ctl_proc *cc; int properlen; int maclen; #ifdef DEBUG if (debug > 2) printf("in process_control()\n"); #endif /* * Save the addresses for error responses */ numctlreq++; rmt_addr = &rbufp->recv_srcadr; lcl_inter = rbufp->dstadr; pkt = (struct ntp_control *)&rbufp->recv_pkt; /* * If the length is less than required for the header, or * it is a response or a fragment, ignore this. */ if (rbufp->recv_length < CTL_HEADER_LEN || pkt->r_m_e_op & (CTL_RESPONSE|CTL_MORE|CTL_ERROR) || pkt->offset != 0) { #ifdef DEBUG if (debug) printf("invalid format in control packet\n"); #endif if (rbufp->recv_length < CTL_HEADER_LEN) numctltooshort++; if (pkt->r_m_e_op & CTL_RESPONSE) numctlinputresp++; if (pkt->r_m_e_op & CTL_MORE) numctlinputfrag++; if (pkt->r_m_e_op & CTL_ERROR) numctlinputerr++; if (pkt->offset != 0) numctlbadoffset++; return; } res_version = PKT_VERSION(pkt->li_vn_mode); if (res_version > NTP_VERSION || res_version < NTP_OLDVERSION) { #ifdef DEBUG if (debug) printf("unknown version %d in control packet\n", res_version); #endif numctlbadversion++; return; } /* * Pull enough data from the packet to make intelligent * responses */ rpkt.li_vn_mode = PKT_LI_VN_MODE(sys_leap, res_version, MODE_CONTROL); res_opcode = pkt->r_m_e_op; rpkt.sequence = pkt->sequence; rpkt.associd = pkt->associd; rpkt.status = 0; res_offset = 0; res_associd = htons(pkt->associd); res_async = 0; res_authenticate = 0; res_keyid = 0; res_authokay = 0; req_count = (int)htons(pkt->count); datanotbinflag = 0; datalinelen = 0; datapt = rpkt.data; dataend = &(rpkt.data[CTL_MAX_DATA_LEN]); /* * We're set up now. Make sure we've got at least enough * incoming data space to match the count. */ req_data = rbufp->recv_length - CTL_HEADER_LEN; if (req_data < req_count || rbufp->recv_length & 0x3) { ctl_error(CERR_BADFMT); numctldatatooshort++; return; } properlen = req_count + CTL_HEADER_LEN; #ifdef DEBUG if (debug > 2 && (rbufp->recv_length & 0x3) != 0) printf("Packet length %d unrounded\n", rbufp->recv_length); #endif /* round up proper len to a 8 octet boundary */ properlen = (properlen + 7) & ~7; maclen = rbufp->recv_length - properlen; if ((rbufp->recv_length & (sizeof(u_long) - 1)) == 0 && maclen >= MIN_MAC_LEN && maclen <= MAX_MAC_LEN && sys_authenticate) { res_authenticate = 1; res_keyid = ntohl(*(u_int32 *)((u_char *)pkt + properlen)); #ifdef DEBUG if (debug > 2) printf( "recv_len %d, properlen %d, wants auth with keyid %08x, MAC length=%d\n", rbufp->recv_length, properlen, res_keyid, maclen); #endif if (!authistrusted(res_keyid)) { #ifdef DEBUG if (debug > 2) printf("invalid keyid %08x\n", res_keyid); #endif } else if (authdecrypt(res_keyid, (u_int32 *)pkt, rbufp->recv_length - maclen, maclen)) { #ifdef DEBUG if (debug > 2) printf("authenticated okay\n"); #endif res_authokay = 1; } else { #ifdef DEBUG if (debug > 2) printf("authentication failed\n"); #endif res_keyid = 0; } } /* * Set up translate pointers */ reqpt = (char *)pkt->data; reqend = reqpt + req_count; /* * Look for the opcode processor */ for (cc = control_codes; cc->control_code != NO_REQUEST; cc++) { if (cc->control_code == res_opcode) { #ifdef DEBUG if (debug > 2) printf("opcode %d, found command handler\n", res_opcode); #endif if (cc->flags == AUTH && (!res_authokay || res_keyid != ctl_auth_keyid)) { ctl_error(CERR_PERMISSION); return; } (cc->handler)(rbufp, restrict_mask); return; } } /* * Can't find this one, return an error. */ numctlbadop++; ctl_error(CERR_BADOP); return; } /* * ctlpeerstatus - return a status word for this peer */ u_short ctlpeerstatus( register struct peer *peer ) { register u_short status; status = peer->status; if (peer->flags & FLAG_CONFIG) status |= CTL_PST_CONFIG; if (peer->flags & FLAG_AUTHENABLE) status |= CTL_PST_AUTHENABLE; if (peer->flags & FLAG_AUTHENTIC) status |= CTL_PST_AUTHENTIC; if (peer->reach != 0) status |= CTL_PST_REACH; return (u_short)CTL_PEER_STATUS(status, peer->num_events, peer->last_event); } /* * ctlclkstatus - return a status word for this clock */ #ifdef REFCLOCK static u_short ctlclkstatus( struct refclockstat *this_clock ) { return ((u_short)(((this_clock->currentstatus) << 8) | (this_clock->lastevent))); } #endif /* * ctlsysstatus - return the system status word */ u_short ctlsysstatus(void) { register u_char this_clock; this_clock = CTL_SST_TS_UNSPEC; #ifdef REFCLOCK if (sys_peer != 0) { if (sys_peer->sstclktype != CTL_SST_TS_UNSPEC) { this_clock = sys_peer->sstclktype; if (pps_control) this_clock |= CTL_SST_TS_PPS; } else { if (sys_peer->refclktype < sizeof(clocktypes)) this_clock = clocktypes[sys_peer->refclktype]; if (pps_control) this_clock |= CTL_SST_TS_PPS; } } #endif /* REFCLOCK */ return (u_short)CTL_SYS_STATUS(sys_leap, this_clock, ctl_sys_num_events, ctl_sys_last_event); } /* * ctl_flushpkt - write out the current packet and prepare * another if necessary. */ static void ctl_flushpkt( int more ) { int dlen; int sendlen; if (!more && datanotbinflag) { /* * Big hack, output a trailing \r\n */ *datapt++ = '\r'; *datapt++ = '\n'; } dlen = datapt - (u_char *)rpkt.data; sendlen = dlen + CTL_HEADER_LEN; /* * Pad to a multiple of 32 bits */ while (sendlen & 0x3) { *datapt++ = '\0'; sendlen++; } /* * Fill in the packet with the current info */ rpkt.r_m_e_op = (u_char)(CTL_RESPONSE|more|(res_opcode & CTL_OP_MASK)); rpkt.count = htons((u_short) dlen); rpkt.offset = htons( (u_short) res_offset); if (res_async) { register int i; for (i = 0; i < CTL_MAXTRAPS; i++) { if (ctl_trap[i].tr_flags & TRAP_INUSE) { rpkt.li_vn_mode = PKT_LI_VN_MODE(sys_leap, ctl_trap[i].tr_version, MODE_CONTROL); rpkt.sequence = htons(ctl_trap[i].tr_sequence); sendpkt(&ctl_trap[i].tr_addr, ctl_trap[i].tr_localaddr, -4, (struct pkt *)&rpkt, sendlen); if (!more) ctl_trap[i].tr_sequence++; numasyncmsgs++; } } } else { if (res_authenticate && sys_authenticate) { int maclen; int totlen = sendlen; keyid_t keyid = htonl(res_keyid); /* * If we are going to authenticate, then there * is an additional requirement that the MAC * begin on a 64 bit boundary. */ while (totlen & 7) { *datapt++ = '\0'; totlen++; } memcpy(datapt, &keyid, sizeof keyid); maclen = authencrypt(res_keyid, (u_int32 *)&rpkt, totlen); sendpkt(rmt_addr, lcl_inter, -5, (struct pkt *)&rpkt, totlen + maclen); } else { sendpkt(rmt_addr, lcl_inter, -6, (struct pkt *)&rpkt, sendlen); } if (more) numctlfrags++; else numctlresponses++; } /* * Set us up for another go around. */ res_offset += dlen; datapt = (u_char *)rpkt.data; } /* * ctl_putdata - write data into the packet, fragmenting and starting * another if this one is full. */ static void ctl_putdata( const char *dp, unsigned int dlen, int bin /* set to 1 when data is binary */ ) { int overhead; + unsigned int currentlen; overhead = 0; if (!bin) { datanotbinflag = 1; overhead = 3; if (datapt != rpkt.data) { *datapt++ = ','; datalinelen++; if ((dlen + datalinelen + 1) >= MAXDATALINELEN) { *datapt++ = '\r'; *datapt++ = '\n'; datalinelen = 0; } else { *datapt++ = ' '; datalinelen++; } } } /* * Save room for trailing junk */ - if (dlen + overhead + datapt > dataend) { + while (dlen + overhead + datapt > dataend) { /* * Not enough room in this one, flush it out. */ + currentlen = MIN(dlen, dataend - datapt); + + memcpy(datapt, dp, currentlen); + + datapt += currentlen; + dp += currentlen; + dlen -= currentlen; + datalinelen += currentlen; + ctl_flushpkt(CTL_MORE); } + memmove((char *)datapt, dp, (unsigned)dlen); datapt += dlen; datalinelen += dlen; } /* * ctl_putstr - write a tagged string into the response packet */ static void ctl_putstr( const char *tag, const char *data, unsigned int len ) { register char *cp; register const char *cq; char buffer[400]; cp = buffer; cq = tag; while (*cq != '\0') *cp++ = *cq++; if (len > 0) { *cp++ = '='; *cp++ = '"'; if (len > (int) (sizeof(buffer) - (cp - buffer) - 1)) len = sizeof(buffer) - (cp - buffer) - 1; memmove(cp, data, (unsigned)len); cp += len; *cp++ = '"'; } ctl_putdata(buffer, (unsigned)( cp - buffer ), 0); } /* * ctl_putdbl - write a tagged, signed double into the response packet */ static void ctl_putdbl( const char *tag, double ts ) { register char *cp; register const char *cq; char buffer[200]; cp = buffer; cq = tag; while (*cq != '\0') *cp++ = *cq++; *cp++ = '='; (void)sprintf(cp, "%.3f", ts); while (*cp != '\0') cp++; ctl_putdata(buffer, (unsigned)( cp - buffer ), 0); } /* * ctl_putuint - write a tagged unsigned integer into the response */ static void ctl_putuint( const char *tag, u_long uval ) { register char *cp; register const char *cq; char buffer[200]; cp = buffer; cq = tag; while (*cq != '\0') *cp++ = *cq++; *cp++ = '='; (void) sprintf(cp, "%lu", uval); while (*cp != '\0') cp++; ctl_putdata(buffer, (unsigned)( cp - buffer ), 0); } /* * ctl_putfs - write a decoded filestamp into the response */ #ifdef OPENSSL static void ctl_putfs( const char *tag, tstamp_t uval ) { register char *cp; register const char *cq; char buffer[200]; struct tm *tm = NULL; time_t fstamp; cp = buffer; cq = tag; while (*cq != '\0') *cp++ = *cq++; *cp++ = '='; fstamp = uval - JAN_1970; tm = gmtime(&fstamp); if (tm == NULL) return; sprintf(cp, "%04d%02d%02d%02d%02d", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min); while (*cp != '\0') cp++; ctl_putdata(buffer, (unsigned)( cp - buffer ), 0); } #endif /* * ctl_puthex - write a tagged unsigned integer, in hex, into the response */ static void ctl_puthex( const char *tag, u_long uval ) { register char *cp; register const char *cq; char buffer[200]; cp = buffer; cq = tag; while (*cq != '\0') *cp++ = *cq++; *cp++ = '='; (void) sprintf(cp, "0x%lx", uval); while (*cp != '\0') cp++; ctl_putdata(buffer,(unsigned)( cp - buffer ), 0); } /* * ctl_putint - write a tagged signed integer into the response */ static void ctl_putint( const char *tag, long ival ) { register char *cp; register const char *cq; char buffer[200]; cp = buffer; cq = tag; while (*cq != '\0') *cp++ = *cq++; *cp++ = '='; (void) sprintf(cp, "%ld", ival); while (*cp != '\0') cp++; ctl_putdata(buffer, (unsigned)( cp - buffer ), 0); } /* * ctl_putts - write a tagged timestamp, in hex, into the response */ static void ctl_putts( const char *tag, l_fp *ts ) { register char *cp; register const char *cq; char buffer[200]; cp = buffer; cq = tag; while (*cq != '\0') *cp++ = *cq++; *cp++ = '='; (void) sprintf(cp, "0x%08lx.%08lx", ts->l_ui & ULONG_CONST(0xffffffff), ts->l_uf & ULONG_CONST(0xffffffff)); while (*cp != '\0') cp++; ctl_putdata(buffer, (unsigned)( cp - buffer ), 0); } /* * ctl_putadr - write an IP address into the response */ static void ctl_putadr( const char *tag, u_int32 addr32, struct sockaddr_storage* addr ) { register char *cp; register const char *cq; char buffer[200]; cp = buffer; cq = tag; while (*cq != '\0') *cp++ = *cq++; *cp++ = '='; if (addr == NULL) cq = numtoa(addr32); else cq = stoa(addr); while (*cq != '\0') *cp++ = *cq++; ctl_putdata(buffer, (unsigned)( cp - buffer ), 0); } /* * ctl_putid - write a tagged clock ID into the response */ static void ctl_putid( const char *tag, char *id ) { register char *cp; register const char *cq; char buffer[200]; cp = buffer; cq = tag; while (*cq != '\0') *cp++ = *cq++; *cp++ = '='; cq = id; while (*cq != '\0' && (cq - id) < 4) *cp++ = *cq++; ctl_putdata(buffer, (unsigned)( cp - buffer ), 0); } /* * ctl_putarray - write a tagged eight element double array into the response */ static void ctl_putarray( const char *tag, double *arr, int start ) { register char *cp; register const char *cq; char buffer[200]; int i; cp = buffer; cq = tag; while (*cq != '\0') *cp++ = *cq++; i = start; do { if (i == 0) i = NTP_SHIFT; i--; (void)sprintf(cp, " %.2f", arr[i] * 1e3); while (*cp != '\0') cp++; } while(i != start); ctl_putdata(buffer, (unsigned)(cp - buffer), 0); } /* * ctl_putsys - output a system variable */ static void ctl_putsys( int varid ) { l_fp tmp; char str[256]; #ifdef OPENSSL struct cert_info *cp; char cbuf[256]; #endif /* OPENSSL */ switch (varid) { case CS_LEAP: ctl_putuint(sys_var[CS_LEAP].text, sys_leap); break; case CS_STRATUM: ctl_putuint(sys_var[CS_STRATUM].text, sys_stratum); break; case CS_PRECISION: ctl_putint(sys_var[CS_PRECISION].text, sys_precision); break; case CS_ROOTDELAY: ctl_putdbl(sys_var[CS_ROOTDELAY].text, sys_rootdelay * 1e3); break; case CS_ROOTDISPERSION: ctl_putdbl(sys_var[CS_ROOTDISPERSION].text, sys_rootdispersion * 1e3); break; case CS_REFID: if (sys_stratum > 1 && sys_stratum < STRATUM_UNSPEC) ctl_putadr(sys_var[CS_REFID].text, sys_refid, NULL); else ctl_putid(sys_var[CS_REFID].text, (char *)&sys_refid); break; case CS_REFTIME: ctl_putts(sys_var[CS_REFTIME].text, &sys_reftime); break; case CS_POLL: ctl_putuint(sys_var[CS_POLL].text, sys_poll); break; case CS_PEERID: if (sys_peer == NULL) ctl_putuint(sys_var[CS_PEERID].text, 0); else ctl_putuint(sys_var[CS_PEERID].text, sys_peer->associd); break; case CS_STATE: ctl_putuint(sys_var[CS_STATE].text, (unsigned)state); break; case CS_OFFSET: ctl_putdbl(sys_var[CS_OFFSET].text, last_offset * 1e3); break; case CS_DRIFT: ctl_putdbl(sys_var[CS_DRIFT].text, drift_comp * 1e6); break; case CS_JITTER: ctl_putdbl(sys_var[CS_JITTER].text, sys_jitter * 1e3); break; case CS_ERROR: ctl_putdbl(sys_var[CS_ERROR].text, clock_jitter * 1e3); break; case CS_CLOCK: get_systime(&tmp); ctl_putts(sys_var[CS_CLOCK].text, &tmp); break; case CS_PROCESSOR: #ifndef HAVE_UNAME ctl_putstr(sys_var[CS_PROCESSOR].text, str_processor, sizeof(str_processor) - 1); #else ctl_putstr(sys_var[CS_PROCESSOR].text, utsnamebuf.machine, strlen(utsnamebuf.machine)); #endif /* HAVE_UNAME */ break; case CS_SYSTEM: #ifndef HAVE_UNAME ctl_putstr(sys_var[CS_SYSTEM].text, str_system, sizeof(str_system) - 1); #else sprintf(str, "%s/%s", utsnamebuf.sysname, utsnamebuf.release); ctl_putstr(sys_var[CS_SYSTEM].text, str, strlen(str)); #endif /* HAVE_UNAME */ break; case CS_VERSION: ctl_putstr(sys_var[CS_VERSION].text, Version, strlen(Version)); break; case CS_STABIL: ctl_putdbl(sys_var[CS_STABIL].text, clock_stability * 1e6); break; case CS_VARLIST: { char buf[CTL_MAX_DATA_LEN]; register char *s, *t, *be; register const char *ss; register int i; register struct ctl_var *k; s = buf; be = buf + sizeof(buf) - strlen(sys_var[CS_VARLIST].text) - 4; if (s > be) break; /* really long var name */ strcpy(s, sys_var[CS_VARLIST].text); strcat(s, "=\""); s += strlen(s); t = s; for (k = sys_var; !(k->flags &EOV); k++) { if (k->flags & PADDING) continue; i = strlen(k->text); if (s+i+1 >= be) break; if (s != t) *s++ = ','; strcpy(s, k->text); s += i; } for (k = ext_sys_var; k && !(k->flags &EOV); k++) { if (k->flags & PADDING) continue; ss = k->text; if (!ss) continue; while (*ss && *ss != '=') ss++; i = ss - k->text; if (s + i + 1 >= be) break; if (s != t) *s++ = ','; strncpy(s, k->text, (unsigned)i); s += i; } if (s+2 >= be) break; *s++ = '"'; *s = '\0'; ctl_putdata(buf, (unsigned)( s - buf ), 0); } break; #ifdef OPENSSL case CS_FLAGS: if (crypto_flags) { ctl_puthex(sys_var[CS_FLAGS].text, crypto_flags); } break; case CS_DIGEST: if (crypto_flags) { const EVP_MD *dp; dp = EVP_get_digestbynid(crypto_flags >> 16); strcpy(str, OBJ_nid2ln(EVP_MD_pkey_type(dp))); ctl_putstr(sys_var[CS_DIGEST].text, str, strlen(str)); } break; case CS_HOST: if (sys_hostname != NULL) ctl_putstr(sys_var[CS_HOST].text, sys_hostname, strlen(sys_hostname)); break; case CS_CERTIF: for (cp = cinfo; cp != NULL; cp = cp->link) { sprintf(cbuf, "%s %s 0x%x", cp->subject, cp->issuer, cp->flags); ctl_putstr(sys_var[CS_CERTIF].text, cbuf, strlen(cbuf)); ctl_putfs(sys_var[CS_REVOKE].text, cp->last); } break; case CS_PUBLIC: if (hostval.fstamp != 0) ctl_putfs(sys_var[CS_PUBLIC].text, ntohl(hostval.tstamp)); break; case CS_REVTIME: if (hostval.tstamp != 0) ctl_putfs(sys_var[CS_REVTIME].text, ntohl(hostval.tstamp)); break; case CS_IDENT: if (iffpar_pkey != NULL) ctl_putstr(sys_var[CS_IDENT].text, iffpar_file, strlen(iffpar_file)); if (gqpar_pkey != NULL) ctl_putstr(sys_var[CS_IDENT].text, gqpar_file, strlen(gqpar_file)); if (mvpar_pkey != NULL) ctl_putstr(sys_var[CS_IDENT].text, mvpar_file, strlen(mvpar_file)); break; case CS_LEAPTAB: if (tai_leap.fstamp != 0) ctl_putfs(sys_var[CS_LEAPTAB].text, ntohl(tai_leap.fstamp)); break; case CS_TAI: ctl_putuint(sys_var[CS_TAI].text, sys_tai); break; #endif /* OPENSSL */ } } /* * ctl_putpeer - output a peer variable */ static void ctl_putpeer( int varid, struct peer *peer ) { int temp; #ifdef OPENSSL char str[256]; struct autokey *ap; #endif /* OPENSSL */ switch (varid) { case CP_CONFIG: ctl_putuint(peer_var[CP_CONFIG].text, (unsigned)((peer->flags & FLAG_CONFIG) != 0)); break; case CP_AUTHENABLE: ctl_putuint(peer_var[CP_AUTHENABLE].text, (unsigned)((peer->flags & FLAG_AUTHENABLE) != 0)); break; case CP_AUTHENTIC: ctl_putuint(peer_var[CP_AUTHENTIC].text, (unsigned)((peer->flags & FLAG_AUTHENTIC) != 0)); break; case CP_SRCADR: ctl_putadr(peer_var[CP_SRCADR].text, 0, &peer->srcadr); break; case CP_SRCPORT: ctl_putuint(peer_var[CP_SRCPORT].text, ntohs(((struct sockaddr_in*)&peer->srcadr)->sin_port)); break; case CP_DSTADR: if (peer->dstadr) { ctl_putadr(peer_var[CP_DSTADR].text, 0, &(peer->dstadr->sin)); } else { ctl_putadr(peer_var[CP_DSTADR].text, 0, NULL); } break; case CP_DSTPORT: ctl_putuint(peer_var[CP_DSTPORT].text, (u_long)(peer->dstadr ? ntohs(((struct sockaddr_in*)&peer->dstadr->sin)->sin_port) : 0)); break; case CP_LEAP: ctl_putuint(peer_var[CP_LEAP].text, peer->leap); break; case CP_HMODE: ctl_putuint(peer_var[CP_HMODE].text, peer->hmode); break; case CP_STRATUM: ctl_putuint(peer_var[CP_STRATUM].text, peer->stratum); break; case CP_PPOLL: ctl_putuint(peer_var[CP_PPOLL].text, peer->ppoll); break; case CP_HPOLL: ctl_putuint(peer_var[CP_HPOLL].text, peer->hpoll); break; case CP_PRECISION: ctl_putint(peer_var[CP_PRECISION].text, peer->precision); break; case CP_ROOTDELAY: ctl_putdbl(peer_var[CP_ROOTDELAY].text, peer->rootdelay * 1e3); break; case CP_ROOTDISPERSION: ctl_putdbl(peer_var[CP_ROOTDISPERSION].text, peer->rootdispersion * 1e3); break; case CP_REFID: if (peer->flags & FLAG_REFCLOCK) { ctl_putid(peer_var[CP_REFID].text, (char *)&peer->refid); } else { if (peer->stratum > 1 && peer->stratum < STRATUM_UNSPEC) ctl_putadr(peer_var[CP_REFID].text, peer->refid, NULL); else ctl_putid(peer_var[CP_REFID].text, (char *)&peer->refid); } break; case CP_REFTIME: ctl_putts(peer_var[CP_REFTIME].text, &peer->reftime); break; case CP_ORG: ctl_putts(peer_var[CP_ORG].text, &peer->org); break; case CP_REC: ctl_putts(peer_var[CP_REC].text, &peer->rec); break; case CP_XMT: ctl_putts(peer_var[CP_XMT].text, &peer->xmt); break; case CP_REACH: ctl_puthex(peer_var[CP_REACH].text, peer->reach); break; case CP_FLASH: temp = peer->flash; ctl_puthex(peer_var[CP_FLASH].text, temp); break; case CP_TTL: ctl_putint(peer_var[CP_TTL].text, sys_ttl[peer->ttl]); break; case CP_UNREACH: ctl_putuint(peer_var[CP_UNREACH].text, peer->unreach); break; case CP_TIMER: ctl_putuint(peer_var[CP_TIMER].text, peer->nextdate - current_time); break; case CP_DELAY: ctl_putdbl(peer_var[CP_DELAY].text, peer->delay * 1e3); break; case CP_OFFSET: ctl_putdbl(peer_var[CP_OFFSET].text, peer->offset * 1e3); break; case CP_JITTER: ctl_putdbl(peer_var[CP_JITTER].text, peer->jitter * 1e3); break; case CP_DISPERSION: ctl_putdbl(peer_var[CP_DISPERSION].text, peer->disp * 1e3); break; case CP_KEYID: ctl_putuint(peer_var[CP_KEYID].text, peer->keyid); break; case CP_FILTDELAY: ctl_putarray(peer_var[CP_FILTDELAY].text, peer->filter_delay, (int)peer->filter_nextpt); break; case CP_FILTOFFSET: ctl_putarray(peer_var[CP_FILTOFFSET].text, peer->filter_offset, (int)peer->filter_nextpt); break; case CP_FILTERROR: ctl_putarray(peer_var[CP_FILTERROR].text, peer->filter_disp, (int)peer->filter_nextpt); break; case CP_PMODE: ctl_putuint(peer_var[CP_PMODE].text, peer->pmode); break; case CP_RECEIVED: ctl_putuint(peer_var[CP_RECEIVED].text, peer->received); break; case CP_SENT: ctl_putuint(peer_var[CP_SENT].text, peer->sent); break; case CP_VARLIST: { char buf[CTL_MAX_DATA_LEN]; register char *s, *t, *be; register int i; register struct ctl_var *k; s = buf; be = buf + sizeof(buf) - strlen(peer_var[CP_VARLIST].text) - 4; if (s > be) break; /* really long var name */ strcpy(s, peer_var[CP_VARLIST].text); strcat(s, "=\""); s += strlen(s); t = s; for (k = peer_var; !(k->flags &EOV); k++) { if (k->flags & PADDING) continue; i = strlen(k->text); if (s + i + 1 >= be) break; if (s != t) *s++ = ','; strcpy(s, k->text); s += i; } if (s+2 >= be) break; *s++ = '"'; *s = '\0'; ctl_putdata(buf, (unsigned)(s - buf), 0); } break; #ifdef OPENSSL case CP_FLAGS: if (peer->crypto) ctl_puthex(peer_var[CP_FLAGS].text, peer->crypto); break; case CP_DIGEST: if (peer->crypto) { const EVP_MD *dp; dp = EVP_get_digestbynid(peer->crypto >> 16); strcpy(str, OBJ_nid2ln(EVP_MD_pkey_type(dp))); ctl_putstr(peer_var[CP_DIGEST].text, str, strlen(str)); } break; case CP_HOST: if (peer->subject != NULL) ctl_putstr(peer_var[CP_HOST].text, peer->subject, strlen(peer->subject)); break; case CP_VALID: /* not used */ break; case CP_IDENT: if (peer->issuer != NULL) ctl_putstr(peer_var[CP_IDENT].text, peer->issuer, strlen(peer->issuer)); break; case CP_INITSEQ: if ((ap = (struct autokey *)peer->recval.ptr) == NULL) break; ctl_putint(peer_var[CP_INITSEQ].text, ap->seq); ctl_puthex(peer_var[CP_INITKEY].text, ap->key); ctl_putfs(peer_var[CP_INITTSP].text, ntohl(peer->recval.tstamp)); break; #endif /* OPENSSL */ } } #ifdef REFCLOCK /* * ctl_putclock - output clock variables */ static void ctl_putclock( int varid, struct refclockstat *clock_stat, int mustput ) { switch(varid) { case CC_TYPE: if (mustput || clock_stat->clockdesc == NULL || *(clock_stat->clockdesc) == '\0') { ctl_putuint(clock_var[CC_TYPE].text, clock_stat->type); } break; case CC_TIMECODE: ctl_putstr(clock_var[CC_TIMECODE].text, clock_stat->p_lastcode, (unsigned)clock_stat->lencode); break; case CC_POLL: ctl_putuint(clock_var[CC_POLL].text, clock_stat->polls); break; case CC_NOREPLY: ctl_putuint(clock_var[CC_NOREPLY].text, clock_stat->noresponse); break; case CC_BADFORMAT: ctl_putuint(clock_var[CC_BADFORMAT].text, clock_stat->badformat); break; case CC_BADDATA: ctl_putuint(clock_var[CC_BADDATA].text, clock_stat->baddata); break; case CC_FUDGETIME1: if (mustput || (clock_stat->haveflags & CLK_HAVETIME1)) ctl_putdbl(clock_var[CC_FUDGETIME1].text, clock_stat->fudgetime1 * 1e3); break; case CC_FUDGETIME2: if (mustput || (clock_stat->haveflags & CLK_HAVETIME2)) ctl_putdbl(clock_var[CC_FUDGETIME2].text, clock_stat->fudgetime2 * 1e3); break; case CC_FUDGEVAL1: if (mustput || (clock_stat->haveflags & CLK_HAVEVAL1)) ctl_putint(clock_var[CC_FUDGEVAL1].text, clock_stat->fudgeval1); break; case CC_FUDGEVAL2: if (mustput || (clock_stat->haveflags & CLK_HAVEVAL2)) { if (clock_stat->fudgeval1 > 1) ctl_putadr(clock_var[CC_FUDGEVAL2].text, (u_int32)clock_stat->fudgeval2, NULL); else ctl_putid(clock_var[CC_FUDGEVAL2].text, (char *)&clock_stat->fudgeval2); } break; case CC_FLAGS: if (mustput || (clock_stat->haveflags & (CLK_HAVEFLAG1 | CLK_HAVEFLAG2 | CLK_HAVEFLAG3 | CLK_HAVEFLAG4))) ctl_putuint(clock_var[CC_FLAGS].text, clock_stat->flags); break; case CC_DEVICE: if (clock_stat->clockdesc == NULL || *(clock_stat->clockdesc) == '\0') { if (mustput) ctl_putstr(clock_var[CC_DEVICE].text, "", 0); } else { ctl_putstr(clock_var[CC_DEVICE].text, clock_stat->clockdesc, strlen(clock_stat->clockdesc)); } break; case CC_VARLIST: { char buf[CTL_MAX_DATA_LEN]; register char *s, *t, *be; register const char *ss; register int i; register struct ctl_var *k; s = buf; be = buf + sizeof(buf); if (s + strlen(clock_var[CC_VARLIST].text) + 4 > be) break; /* really long var name */ strcpy(s, clock_var[CC_VARLIST].text); strcat(s, "=\""); s += strlen(s); t = s; for (k = clock_var; !(k->flags &EOV); k++) { if (k->flags & PADDING) continue; i = strlen(k->text); if (s + i + 1 >= be) break; if (s != t) *s++ = ','; strcpy(s, k->text); s += i; } for (k = clock_stat->kv_list; k && !(k->flags & EOV); k++) { if (k->flags & PADDING) continue; ss = k->text; if (!ss) continue; while (*ss && *ss != '=') ss++; i = ss - k->text; if (s+i+1 >= be) break; if (s != t) *s++ = ','; strncpy(s, k->text, (unsigned)i); s += i; *s = '\0'; } if (s+2 >= be) break; *s++ = '"'; *s = '\0'; ctl_putdata(buf, (unsigned)( s - buf ), 0); } break; } } #endif /* * ctl_getitem - get the next data item from the incoming packet */ static struct ctl_var * ctl_getitem( struct ctl_var *var_list, char **data ) { register struct ctl_var *v; register char *cp; register char *tp; static struct ctl_var eol = { 0, EOV, }; static char buf[128]; /* * Delete leading commas and white space */ while (reqpt < reqend && (*reqpt == ',' || isspace((unsigned char)*reqpt))) reqpt++; if (reqpt >= reqend) return (0); if (var_list == (struct ctl_var *)0) return (&eol); /* * Look for a first character match on the tag. If we find * one, see if it is a full match. */ v = var_list; cp = reqpt; while (!(v->flags & EOV)) { if (!(v->flags & PADDING) && *cp == *(v->text)) { tp = v->text; while (*tp != '\0' && *tp != '=' && cp < reqend && *cp == *tp) { cp++; tp++; } if ((*tp == '\0') || (*tp == '=')) { while (cp < reqend && isspace((unsigned char)*cp)) cp++; if (cp == reqend || *cp == ',') { buf[0] = '\0'; *data = buf; if (cp < reqend) cp++; reqpt = cp; return v; } if (*cp == '=') { cp++; tp = buf; while (cp < reqend && isspace((unsigned char)*cp)) cp++; while (cp < reqend && *cp != ',') { *tp++ = *cp++; if (tp >= buf + sizeof(buf)) { ctl_error(CERR_BADFMT); numctlbadpkts++; #if 0 /* Avoid possible DOS attack */ /* If we get a smarter msyslog we can re-enable this */ msyslog(LOG_WARNING, "Possible 'ntpdx' exploit from %s:%d (possibly spoofed)\n", stoa(rmt_addr), SRCPORT(rmt_addr) ); #endif return (0); } } if (cp < reqend) cp++; *tp-- = '\0'; while (tp >= buf) { if (!isspace((unsigned int)(*tp))) break; *tp-- = '\0'; } reqpt = cp; *data = buf; return (v); } } cp = reqpt; } v++; } return v; } /* * control_unspec - response to an unspecified op-code */ /*ARGSUSED*/ static void control_unspec( struct recvbuf *rbufp, int restrict_mask ) { struct peer *peer; /* * What is an appropriate response to an unspecified op-code? * I return no errors and no data, unless a specified assocation * doesn't exist. */ if (res_associd != 0) { if ((peer = findpeerbyassoc(res_associd)) == 0) { ctl_error(CERR_BADASSOC); return; } rpkt.status = htons(ctlpeerstatus(peer)); } else { rpkt.status = htons(ctlsysstatus()); } ctl_flushpkt(0); } /* * read_status - return either a list of associd's, or a particular * peer's status. */ /*ARGSUSED*/ static void read_status( struct recvbuf *rbufp, int restrict_mask ) { register int i; register struct peer *peer; u_short ass_stat[CTL_MAX_DATA_LEN / sizeof(u_short)]; #ifdef DEBUG if (debug > 2) printf("read_status: ID %d\n", res_associd); #endif /* * Two choices here. If the specified association ID is * zero we return all known assocation ID's. Otherwise * we return a bunch of stuff about the particular peer. */ if (res_associd == 0) { register int n; n = 0; rpkt.status = htons(ctlsysstatus()); for (i = 0; i < NTP_HASH_SIZE; i++) { for (peer = assoc_hash[i]; peer != 0; peer = peer->ass_next) { ass_stat[n++] = htons(peer->associd); ass_stat[n++] = htons(ctlpeerstatus(peer)); if (n == CTL_MAX_DATA_LEN/sizeof(u_short)) { ctl_putdata((char *)ass_stat, n * sizeof(u_short), 1); n = 0; } } } if (n != 0) ctl_putdata((char *)ass_stat, n * sizeof(u_short), 1); ctl_flushpkt(0); } else { peer = findpeerbyassoc(res_associd); if (peer == 0) { ctl_error(CERR_BADASSOC); } else { register u_char *cp; rpkt.status = htons(ctlpeerstatus(peer)); if (res_authokay) peer->num_events = 0; /* * For now, output everything we know about the * peer. May be more selective later. */ for (cp = def_peer_var; *cp != 0; cp++) ctl_putpeer((int)*cp, peer); ctl_flushpkt(0); } } } /* * read_variables - return the variables the caller asks for */ /*ARGSUSED*/ static void read_variables( struct recvbuf *rbufp, int restrict_mask ) { register struct ctl_var *v; register int i; char *valuep; u_char *wants; unsigned int gotvar = (CS_MAXCODE > CP_MAXCODE) ? (CS_MAXCODE + 1) : (CP_MAXCODE + 1); if (res_associd == 0) { /* * Wants system variables. Figure out which he wants * and give them to him. */ rpkt.status = htons(ctlsysstatus()); if (res_authokay) ctl_sys_num_events = 0; gotvar += count_var(ext_sys_var); wants = (u_char *)emalloc(gotvar); memset((char *)wants, 0, gotvar); gotvar = 0; while ((v = ctl_getitem(sys_var, &valuep)) != 0) { if (v->flags & EOV) { if ((v = ctl_getitem(ext_sys_var, &valuep)) != 0) { if (v->flags & EOV) { ctl_error(CERR_UNKNOWNVAR); free((char *)wants); return; } wants[CS_MAXCODE + 1 + v->code] = 1; gotvar = 1; continue; } else { break; /* shouldn't happen ! */ } } wants[v->code] = 1; gotvar = 1; } if (gotvar) { for (i = 1; i <= CS_MAXCODE; i++) if (wants[i]) ctl_putsys(i); for (i = 0; ext_sys_var && !(ext_sys_var[i].flags & EOV); i++) if (wants[i + CS_MAXCODE + 1]) ctl_putdata(ext_sys_var[i].text, strlen(ext_sys_var[i].text), 0); } else { register u_char *cs; register struct ctl_var *kv; for (cs = def_sys_var; *cs != 0; cs++) ctl_putsys((int)*cs); for (kv = ext_sys_var; kv && !(kv->flags & EOV); kv++) if (kv->flags & DEF) ctl_putdata(kv->text, strlen(kv->text), 0); } free((char *)wants); } else { register struct peer *peer; /* * Wants info for a particular peer. See if we know * the guy. */ peer = findpeerbyassoc(res_associd); if (peer == 0) { ctl_error(CERR_BADASSOC); return; } rpkt.status = htons(ctlpeerstatus(peer)); if (res_authokay) peer->num_events = 0; wants = (u_char *)emalloc(gotvar); memset((char*)wants, 0, gotvar); gotvar = 0; while ((v = ctl_getitem(peer_var, &valuep)) != 0) { if (v->flags & EOV) { ctl_error(CERR_UNKNOWNVAR); free((char *)wants); return; } wants[v->code] = 1; gotvar = 1; } if (gotvar) { for (i = 1; i <= CP_MAXCODE; i++) if (wants[i]) ctl_putpeer(i, peer); } else { register u_char *cp; for (cp = def_peer_var; *cp != 0; cp++) ctl_putpeer((int)*cp, peer); } free((char *)wants); } ctl_flushpkt(0); } /* * write_variables - write into variables. We only allow leap bit * writing this way. */ /*ARGSUSED*/ static void write_variables( struct recvbuf *rbufp, int restrict_mask ) { register struct ctl_var *v; register int ext_var; char *valuep; long val = 0; /* * If he's trying to write into a peer tell him no way */ if (res_associd != 0) { ctl_error(CERR_PERMISSION); return; } /* * Set status */ rpkt.status = htons(ctlsysstatus()); /* * Look through the variables. Dump out at the first sign of * trouble. */ while ((v = ctl_getitem(sys_var, &valuep)) != 0) { ext_var = 0; if (v->flags & EOV) { if ((v = ctl_getitem(ext_sys_var, &valuep)) != 0) { if (v->flags & EOV) { ctl_error(CERR_UNKNOWNVAR); return; } ext_var = 1; } else { break; } } if (!(v->flags & CAN_WRITE)) { ctl_error(CERR_PERMISSION); return; } if (!ext_var && (*valuep == '\0' || !atoint(valuep, &val))) { ctl_error(CERR_BADFMT); return; } if (!ext_var && (val & ~LEAP_NOTINSYNC) != 0) { ctl_error(CERR_BADVALUE); return; } if (ext_var) { char *s = (char *)emalloc(strlen(v->text) + strlen(valuep) + 2); const char *t; char *tt = s; t = v->text; while (*t && *t != '=') *tt++ = *t++; *tt++ = '='; strcat(tt, valuep); set_sys_var(s, strlen(s)+1, v->flags); free(s); } else { /* * This one seems sane. Save it. */ switch(v->code) { case CS_LEAP: default: ctl_error(CERR_UNSPEC); /* really */ return; } } } /* * If we got anything, do it. xxx nothing to do *** */ /* if (leapind != ~0 || leapwarn != ~0) { if (!leap_setleap((int)leapind, (int)leapwarn)) { ctl_error(CERR_PERMISSION); return; } } */ ctl_flushpkt(0); } /* * read_clock_status - return clock radio status */ /*ARGSUSED*/ static void read_clock_status( struct recvbuf *rbufp, int restrict_mask ) { #ifndef REFCLOCK /* * If no refclock support, no data to return */ ctl_error(CERR_BADASSOC); #else register struct ctl_var *v; register int i; register struct peer *peer; char *valuep; u_char *wants; unsigned int gotvar; struct refclockstat clock_stat; if (res_associd == 0) { /* * Find a clock for this jerk. If the system peer * is a clock use it, else search the hash tables * for one. */ if (sys_peer != 0 && (sys_peer->flags & FLAG_REFCLOCK)) { peer = sys_peer; } else { peer = 0; for (i = 0; peer == 0 && i < NTP_HASH_SIZE; i++) { for (peer = assoc_hash[i]; peer != 0; peer = peer->ass_next) { if (peer->flags & FLAG_REFCLOCK) break; } } if (peer == 0) { ctl_error(CERR_BADASSOC); return; } } } else { peer = findpeerbyassoc(res_associd); if (peer == 0 || !(peer->flags & FLAG_REFCLOCK)) { ctl_error(CERR_BADASSOC); return; } } /* * If we got here we have a peer which is a clock. Get his * status. */ clock_stat.kv_list = (struct ctl_var *)0; refclock_control(&peer->srcadr, (struct refclockstat *)0, &clock_stat); /* * Look for variables in the packet. */ rpkt.status = htons(ctlclkstatus(&clock_stat)); gotvar = CC_MAXCODE + 1 + count_var(clock_stat.kv_list); wants = (u_char *)emalloc(gotvar); memset((char*)wants, 0, gotvar); gotvar = 0; while ((v = ctl_getitem(clock_var, &valuep)) != 0) { if (v->flags & EOV) { if ((v = ctl_getitem(clock_stat.kv_list, &valuep)) != 0) { if (v->flags & EOV) { ctl_error(CERR_UNKNOWNVAR); free((char*)wants); free_varlist(clock_stat.kv_list); return; } wants[CC_MAXCODE + 1 + v->code] = 1; gotvar = 1; continue; } else { break; /* shouldn't happen ! */ } } wants[v->code] = 1; gotvar = 1; } if (gotvar) { for (i = 1; i <= CC_MAXCODE; i++) if (wants[i]) ctl_putclock(i, &clock_stat, 1); for (i = 0; clock_stat.kv_list && !(clock_stat.kv_list[i].flags & EOV); i++) if (wants[i + CC_MAXCODE + 1]) ctl_putdata(clock_stat.kv_list[i].text, strlen(clock_stat.kv_list[i].text), 0); } else { register u_char *cc; register struct ctl_var *kv; for (cc = def_clock_var; *cc != 0; cc++) ctl_putclock((int)*cc, &clock_stat, 0); for (kv = clock_stat.kv_list; kv && !(kv->flags & EOV); kv++) if (kv->flags & DEF) ctl_putdata(kv->text, strlen(kv->text), 0); } free((char*)wants); free_varlist(clock_stat.kv_list); ctl_flushpkt(0); #endif } /* * write_clock_status - we don't do this */ /*ARGSUSED*/ static void write_clock_status( struct recvbuf *rbufp, int restrict_mask ) { ctl_error(CERR_PERMISSION); } /* * Trap support from here on down. We send async trap messages when the * upper levels report trouble. Traps can by set either by control * messages or by configuration. */ /* * set_trap - set a trap in response to a control message */ static void set_trap( struct recvbuf *rbufp, int restrict_mask ) { int traptype; /* * See if this guy is allowed */ if (restrict_mask & RES_NOTRAP) { ctl_error(CERR_PERMISSION); return; } /* * Determine his allowed trap type. */ traptype = TRAP_TYPE_PRIO; if (restrict_mask & RES_LPTRAP) traptype = TRAP_TYPE_NONPRIO; /* * Call ctlsettrap() to do the work. Return * an error if it can't assign the trap. */ if (!ctlsettrap(&rbufp->recv_srcadr, rbufp->dstadr, traptype, (int)res_version)) ctl_error(CERR_NORESOURCE); ctl_flushpkt(0); } /* * unset_trap - unset a trap in response to a control message */ static void unset_trap( struct recvbuf *rbufp, int restrict_mask ) { int traptype; /* * We don't prevent anyone from removing his own trap unless the * trap is configured. Note we also must be aware of the * possibility that restriction flags were changed since this * guy last set his trap. Set the trap type based on this. */ traptype = TRAP_TYPE_PRIO; if (restrict_mask & RES_LPTRAP) traptype = TRAP_TYPE_NONPRIO; /* * Call ctlclrtrap() to clear this out. */ if (!ctlclrtrap(&rbufp->recv_srcadr, rbufp->dstadr, traptype)) ctl_error(CERR_BADASSOC); ctl_flushpkt(0); } /* * ctlsettrap - called to set a trap */ int ctlsettrap( struct sockaddr_storage *raddr, struct interface *linter, int traptype, int version ) { register struct ctl_trap *tp; register struct ctl_trap *tptouse; /* * See if we can find this trap. If so, we only need update * the flags and the time. */ if ((tp = ctlfindtrap(raddr, linter)) != NULL) { switch (traptype) { case TRAP_TYPE_CONFIG: tp->tr_flags = TRAP_INUSE|TRAP_CONFIGURED; break; case TRAP_TYPE_PRIO: if (tp->tr_flags & TRAP_CONFIGURED) return (1); /* don't change anything */ tp->tr_flags = TRAP_INUSE; break; case TRAP_TYPE_NONPRIO: if (tp->tr_flags & TRAP_CONFIGURED) return (1); /* don't change anything */ tp->tr_flags = TRAP_INUSE|TRAP_NONPRIO; break; } tp->tr_settime = current_time; tp->tr_resets++; return (1); } /* * First we heard of this guy. Try to find a trap structure * for him to use, clearing out lesser priority guys if we * have to. Clear out anyone who's expired while we're at it. */ tptouse = NULL; for (tp = ctl_trap; tp < &ctl_trap[CTL_MAXTRAPS]; tp++) { if ((tp->tr_flags & TRAP_INUSE) && !(tp->tr_flags & TRAP_CONFIGURED) && ((tp->tr_settime + CTL_TRAPTIME) > current_time)) { tp->tr_flags = 0; num_ctl_traps--; } if (!(tp->tr_flags & TRAP_INUSE)) { tptouse = tp; } else if (!(tp->tr_flags & TRAP_CONFIGURED)) { switch (traptype) { case TRAP_TYPE_CONFIG: if (tptouse == NULL) { tptouse = tp; break; } if (tptouse->tr_flags & TRAP_NONPRIO && !(tp->tr_flags & TRAP_NONPRIO)) break; if (!(tptouse->tr_flags & TRAP_NONPRIO) && tp->tr_flags & TRAP_NONPRIO) { tptouse = tp; break; } if (tptouse->tr_origtime < tp->tr_origtime) tptouse = tp; break; case TRAP_TYPE_PRIO: if (tp->tr_flags & TRAP_NONPRIO) { if (tptouse == NULL || (tptouse->tr_flags & TRAP_INUSE && tptouse->tr_origtime < tp->tr_origtime)) tptouse = tp; } break; case TRAP_TYPE_NONPRIO: break; } } } /* * If we don't have room for him return an error. */ if (tptouse == NULL) return (0); /* * Set up this structure for him. */ tptouse->tr_settime = tptouse->tr_origtime = current_time; tptouse->tr_count = tptouse->tr_resets = 0; tptouse->tr_sequence = 1; tptouse->tr_addr = *raddr; tptouse->tr_localaddr = linter; tptouse->tr_version = (u_char) version; tptouse->tr_flags = TRAP_INUSE; if (traptype == TRAP_TYPE_CONFIG) tptouse->tr_flags |= TRAP_CONFIGURED; else if (traptype == TRAP_TYPE_NONPRIO) tptouse->tr_flags |= TRAP_NONPRIO; num_ctl_traps++; return (1); } /* * ctlclrtrap - called to clear a trap */ int ctlclrtrap( struct sockaddr_storage *raddr, struct interface *linter, int traptype ) { register struct ctl_trap *tp; if ((tp = ctlfindtrap(raddr, linter)) == NULL) return (0); if (tp->tr_flags & TRAP_CONFIGURED && traptype != TRAP_TYPE_CONFIG) return (0); tp->tr_flags = 0; num_ctl_traps--; return (1); } /* * ctlfindtrap - find a trap given the remote and local addresses */ static struct ctl_trap * ctlfindtrap( struct sockaddr_storage *raddr, struct interface *linter ) { register struct ctl_trap *tp; for (tp = ctl_trap; tp < &ctl_trap[CTL_MAXTRAPS]; tp++) { if ((tp->tr_flags & TRAP_INUSE) && (NSRCPORT(raddr) == NSRCPORT(&tp->tr_addr)) && SOCKCMP(raddr, &tp->tr_addr) && (linter == tp->tr_localaddr) ) return (tp); } return (struct ctl_trap *)NULL; } /* * report_event - report an event to the trappers */ void report_event( int err, struct peer *peer ) { register int i; /* * Record error code in proper spots, but have mercy on the * log file. */ if (!(err & (PEER_EVENT | CRPT_EVENT))) { if (ctl_sys_num_events < CTL_SYS_MAXEVENTS) ctl_sys_num_events++; if (ctl_sys_last_event != (u_char)err) { NLOG(NLOG_SYSEVENT) msyslog(LOG_INFO, "system event '%s' (0x%02x) status '%s' (0x%02x)", eventstr(err), err, sysstatstr(ctlsysstatus()), ctlsysstatus()); #ifdef DEBUG if (debug) printf("report_event: system event '%s' (0x%02x) status '%s' (0x%02x)\n", eventstr(err), err, sysstatstr(ctlsysstatus()), ctlsysstatus()); #endif ctl_sys_last_event = (u_char)err; } } else if (peer != 0) { char *src; #ifdef REFCLOCK if (ISREFCLOCKADR(&peer->srcadr)) src = refnumtoa(&peer->srcadr); else #endif src = stoa(&peer->srcadr); peer->last_event = (u_char)(err & ~PEER_EVENT); if (peer->num_events < CTL_PEER_MAXEVENTS) peer->num_events++; NLOG(NLOG_PEEREVENT) msyslog(LOG_INFO, "peer %s event '%s' (0x%02x) status '%s' (0x%02x)", src, eventstr(err), err, peerstatstr(ctlpeerstatus(peer)), ctlpeerstatus(peer)); #ifdef DEBUG if (debug) printf( "peer %s event '%s' (0x%02x) status '%s' (0x%02x)\n", src, eventstr(err), err, peerstatstr(ctlpeerstatus(peer)), ctlpeerstatus(peer)); #endif } else { msyslog(LOG_ERR, "report_event: err '%s' (0x%02x), no peer", eventstr(err), err); #ifdef DEBUG printf( "report_event: peer event '%s' (0x%02x), no peer\n", eventstr(err), err); #endif return; } /* * If no trappers, return. */ if (num_ctl_traps <= 0) return; /* * Set up the outgoing packet variables */ res_opcode = CTL_OP_ASYNCMSG; res_offset = 0; res_async = 1; res_authenticate = 0; datapt = rpkt.data; dataend = &(rpkt.data[CTL_MAX_DATA_LEN]); if (!(err & PEER_EVENT)) { rpkt.associd = 0; rpkt.status = htons(ctlsysstatus()); /* * For now, put everything we know about system * variables. Don't send crypto strings. */ for (i = 1; i <= CS_MAXCODE; i++) { #ifdef OPENSSL if (i > CS_VARLIST) continue; #endif /* OPENSSL */ ctl_putsys(i); } #ifdef REFCLOCK /* * for clock exception events: add clock variables to * reflect info on exception */ if (err == EVNT_CLOCKEXCPT) { struct refclockstat clock_stat; struct ctl_var *kv; clock_stat.kv_list = (struct ctl_var *)0; refclock_control(&peer->srcadr, (struct refclockstat *)0, &clock_stat); ctl_puthex("refclockstatus", ctlclkstatus(&clock_stat)); for (i = 1; i <= CC_MAXCODE; i++) ctl_putclock(i, &clock_stat, 0); for (kv = clock_stat.kv_list; kv && !(kv->flags & EOV); kv++) if (kv->flags & DEF) ctl_putdata(kv->text, strlen(kv->text), 0); free_varlist(clock_stat.kv_list); } #endif /* REFCLOCK */ } else { rpkt.associd = htons(peer->associd); rpkt.status = htons(ctlpeerstatus(peer)); /* * Dump it all. Later, maybe less. */ for (i = 1; i <= CP_MAXCODE; i++) { #ifdef OPENSSL if (i > CP_VARLIST) continue; #endif /* OPENSSL */ ctl_putpeer(i, peer); } #ifdef REFCLOCK /* * for clock exception events: add clock variables to * reflect info on exception */ if (err == EVNT_PEERCLOCK) { struct refclockstat clock_stat; struct ctl_var *kv; clock_stat.kv_list = (struct ctl_var *)0; refclock_control(&peer->srcadr, (struct refclockstat *)0, &clock_stat); ctl_puthex("refclockstatus", ctlclkstatus(&clock_stat)); for (i = 1; i <= CC_MAXCODE; i++) ctl_putclock(i, &clock_stat, 0); for (kv = clock_stat.kv_list; kv && !(kv->flags & EOV); kv++) if (kv->flags & DEF) ctl_putdata(kv->text, strlen(kv->text), 0); free_varlist(clock_stat.kv_list); } #endif /* REFCLOCK */ } /* * We're done, return. */ ctl_flushpkt(0); } /* * ctl_clr_stats - clear stat counters */ void ctl_clr_stats(void) { ctltimereset = current_time; numctlreq = 0; numctlbadpkts = 0; numctlresponses = 0; numctlfrags = 0; numctlerrors = 0; numctlfrags = 0; numctltooshort = 0; numctlinputresp = 0; numctlinputfrag = 0; numctlinputerr = 0; numctlbadoffset = 0; numctlbadversion = 0; numctldatatooshort = 0; numctlbadop = 0; numasyncmsgs = 0; } static u_long count_var( struct ctl_var *k ) { register u_long c; if (!k) return (0); c = 0; while (!(k++->flags & EOV)) c++; return (c); } char * add_var( struct ctl_var **kv, u_long size, u_short def ) { register u_long c; register struct ctl_var *k; c = count_var(*kv); k = *kv; *kv = (struct ctl_var *)emalloc((c+2)*sizeof(struct ctl_var)); if (k) { memmove((char *)*kv, (char *)k, sizeof(struct ctl_var)*c); free((char *)k); } (*kv)[c].code = (u_short) c; (*kv)[c].text = (char *)emalloc(size); (*kv)[c].flags = def; (*kv)[c+1].code = 0; (*kv)[c+1].text = (char *)0; (*kv)[c+1].flags = EOV; return (char *)(*kv)[c].text; } void set_var( struct ctl_var **kv, const char *data, u_long size, u_short def ) { register struct ctl_var *k; register const char *s; register const char *t; char *td; if (!data || !size) return; k = *kv; if (k != NULL) { while (!(k->flags & EOV)) { s = data; t = k->text; if (t) { while (*t != '=' && *s - *t == 0) { s++; t++; } if (*s == *t && ((*t == '=') || !*t)) { free((void *)k->text); td = (char *)emalloc(size); memmove(td, data, size); k->text =td; k->flags = def; return; } } else { td = (char *)emalloc(size); memmove(td, data, size); k->text = td; k->flags = def; return; } k++; } } td = add_var(kv, size, def); memmove(td, data, size); } void set_sys_var( const char *data, u_long size, u_short def ) { set_var(&ext_sys_var, data, size, def); } void free_varlist( struct ctl_var *kv ) { struct ctl_var *k; if (kv) { for (k = kv; !(k->flags & EOV); k++) free((void *)k->text); free((void *)kv); } } Index: head/contrib/ntp/ntpd/ntp_crypto.c =================================================================== --- head/contrib/ntp/ntpd/ntp_crypto.c (revision 276070) +++ head/contrib/ntp/ntpd/ntp_crypto.c (revision 276071) @@ -1,4189 +1,4201 @@ /* * ntp_crypto.c - NTP version 4 public key routines */ #ifdef HAVE_CONFIG_H #include #endif #ifdef OPENSSL #include #include #include #include #include #include "ntpd.h" #include "ntp_stdlib.h" #include "ntp_unixtime.h" #include "ntp_string.h" #include #include "openssl/asn1_mac.h" #include "openssl/bn.h" #include "openssl/err.h" #include "openssl/evp.h" #include "openssl/pem.h" #include "openssl/rand.h" #include "openssl/x509v3.h" #ifdef KERNEL_PLL #include "ntp_syscall.h" #endif /* KERNEL_PLL */ /* * Extension field message format * * These are always signed and saved before sending in network byte * order. They must be converted to and from host byte order for * processing. * * +-------+-------+ * | op | len | <- extension pointer * +-------+-------+ * | assocID | * +---------------+ * | timestamp | <- value pointer * +---------------+ * | filestamp | * +---------------+ * | value len | * +---------------+ * | | * = value = * | | * +---------------+ * | signature len | * +---------------+ * | | * = signature = * | | * +---------------+ * * The CRYPTO_RESP bit is set to 0 for requests, 1 for responses. * Requests carry the association ID of the receiver; responses carry * the association ID of the sender. Some messages include only the * operation/length and association ID words and so have length 8 * octets. Ohers include the value structure and associated value and * signature fields. These messages include the timestamp, filestamp, * value and signature words and so have length at least 24 octets. The * signature and/or value fields can be empty, in which case the * respective length words are zero. An empty value with nonempty * signature is syntactically valid, but semantically questionable. * * The filestamp represents the time when a cryptographic data file such * as a public/private key pair is created. It follows every reference * depending on that file and serves as a means to obsolete earlier data * of the same type. The timestamp represents the time when the * cryptographic data of the message were last signed. Creation of a * cryptographic data file or signing a message can occur only when the * creator or signor is synchronized to an authoritative source and * proventicated to a trusted authority. * * Note there are four conditions required for server trust. First, the * public key on the certificate must be verified, which involves a * number of format, content and consistency checks. Next, the server * identity must be confirmed by one of four schemes: private * certificate, IFF scheme, GQ scheme or certificate trail hike to a * self signed trusted certificate. Finally, the server signature must * be verified. */ /* * Cryptodefines */ #define TAI_1972 10 /* initial TAI offset (s) */ #define MAX_LEAP 100 /* max UTC leapseconds (s) */ #define VALUE_LEN (6 * 4) /* min response field length */ #define YEAR (60 * 60 * 24 * 365) /* seconds in year */ /* * Global cryptodata in host byte order */ u_int32 crypto_flags = 0x0; /* status word */ /* * Global cryptodata in network byte order */ struct cert_info *cinfo = NULL; /* certificate info/value */ struct value hostval; /* host value */ struct value pubkey; /* public key */ struct value tai_leap; /* leapseconds table */ EVP_PKEY *iffpar_pkey = NULL; /* IFF parameters */ EVP_PKEY *gqpar_pkey = NULL; /* GQ parameters */ EVP_PKEY *mvpar_pkey = NULL; /* MV parameters */ char *iffpar_file = NULL; /* IFF parameters file */ char *gqpar_file = NULL; /* GQ parameters file */ char *mvpar_file = NULL; /* MV parameters file */ /* * Private cryptodata in host byte order */ static char *passwd = NULL; /* private key password */ static EVP_PKEY *host_pkey = NULL; /* host key */ static EVP_PKEY *sign_pkey = NULL; /* sign key */ static const EVP_MD *sign_digest = NULL; /* sign digest */ static u_int sign_siglen; /* sign key length */ static char *rand_file = NULL; /* random seed file */ static char *host_file = NULL; /* host key file */ static char *sign_file = NULL; /* sign key file */ static char *cert_file = NULL; /* certificate file */ static char *leap_file = NULL; /* leapseconds file */ static tstamp_t if_fstamp = 0; /* IFF filestamp */ static tstamp_t gq_fstamp = 0; /* GQ file stamp */ static tstamp_t mv_fstamp = 0; /* MV filestamp */ static u_int ident_scheme = 0; /* server identity scheme */ /* * Cryptotypes */ static int crypto_verify P((struct exten *, struct value *, struct peer *)); static int crypto_encrypt P((struct exten *, struct value *, keyid_t *)); static int crypto_alice P((struct peer *, struct value *)); static int crypto_alice2 P((struct peer *, struct value *)); static int crypto_alice3 P((struct peer *, struct value *)); static int crypto_bob P((struct exten *, struct value *)); static int crypto_bob2 P((struct exten *, struct value *)); static int crypto_bob3 P((struct exten *, struct value *)); static int crypto_iff P((struct exten *, struct peer *)); static int crypto_gq P((struct exten *, struct peer *)); static int crypto_mv P((struct exten *, struct peer *)); static u_int crypto_send P((struct exten *, struct value *)); static tstamp_t crypto_time P((void)); static u_long asn2ntp P((ASN1_TIME *)); static struct cert_info *cert_parse P((u_char *, u_int, tstamp_t)); static int cert_sign P((struct exten *, struct value *)); static int cert_valid P((struct cert_info *, EVP_PKEY *)); static int cert_install P((struct exten *, struct peer *)); static void cert_free P((struct cert_info *)); static EVP_PKEY *crypto_key P((char *, tstamp_t *)); static int bighash P((BIGNUM *, BIGNUM *)); static struct cert_info *crypto_cert P((char *)); static void crypto_tai P((char *)); #ifdef SYS_WINNT int readlink(char * link, char * file, int len) { return (-1); } #endif /* * session_key - generate session key * * This routine generates a session key from the source address, * destination address, key ID and private value. The value of the * session key is the MD5 hash of these values, while the next key ID is * the first four octets of the hash. * * Returns the next key ID */ keyid_t session_key( struct sockaddr_storage *srcadr, /* source address */ struct sockaddr_storage *dstadr, /* destination address */ keyid_t keyno, /* key ID */ keyid_t private, /* private value */ u_long lifetime /* key lifetime */ ) { EVP_MD_CTX ctx; /* message digest context */ u_char dgst[EVP_MAX_MD_SIZE]; /* message digest */ keyid_t keyid; /* key identifer */ u_int32 header[10]; /* data in network byte order */ u_int hdlen, len; if (!dstadr) return 0; /* * Generate the session key and key ID. If the lifetime is * greater than zero, install the key and call it trusted. */ hdlen = 0; switch(srcadr->ss_family) { case AF_INET: header[0] = ((struct sockaddr_in *)srcadr)->sin_addr.s_addr; header[1] = ((struct sockaddr_in *)dstadr)->sin_addr.s_addr; header[2] = htonl(keyno); header[3] = htonl(private); hdlen = 4 * sizeof(u_int32); break; case AF_INET6: memcpy(&header[0], &GET_INADDR6(*srcadr), sizeof(struct in6_addr)); memcpy(&header[4], &GET_INADDR6(*dstadr), sizeof(struct in6_addr)); header[8] = htonl(keyno); header[9] = htonl(private); hdlen = 10 * sizeof(u_int32); break; } EVP_DigestInit(&ctx, EVP_md5()); EVP_DigestUpdate(&ctx, (u_char *)header, hdlen); EVP_DigestFinal(&ctx, dgst, &len); memcpy(&keyid, dgst, 4); keyid = ntohl(keyid); if (lifetime != 0) { MD5auth_setkey(keyno, dgst, len); authtrust(keyno, lifetime); } #ifdef DEBUG if (debug > 1) printf( "session_key: %s > %s %08x %08x hash %08x life %lu\n", stoa(srcadr), stoa(dstadr), keyno, private, keyid, lifetime); #endif return (keyid); } /* * make_keylist - generate key list * * Returns * XEVNT_OK success * XEVNT_PER host certificate expired * * This routine constructs a pseudo-random sequence by repeatedly * hashing the session key starting from a given source address, * destination address, private value and the next key ID of the * preceeding session key. The last entry on the list is saved along * with its sequence number and public signature. */ int make_keylist( struct peer *peer, /* peer structure pointer */ struct interface *dstadr /* interface */ ) { EVP_MD_CTX ctx; /* signature context */ tstamp_t tstamp; /* NTP timestamp */ struct autokey *ap; /* autokey pointer */ struct value *vp; /* value pointer */ keyid_t keyid = 0; /* next key ID */ keyid_t cookie; /* private value */ u_long lifetime; u_int len, mpoll; int i; if (!dstadr) return XEVNT_OK; /* * Allocate the key list if necessary. */ tstamp = crypto_time(); if (peer->keylist == NULL) peer->keylist = emalloc(sizeof(keyid_t) * NTP_MAXSESSION); /* * Generate an initial key ID which is unique and greater than * NTP_MAXKEY. */ while (1) { keyid = (ntp_random() + NTP_MAXKEY + 1) & ((1 << sizeof(keyid_t)) - 1); if (authhavekey(keyid)) continue; break; } /* * Generate up to NTP_MAXSESSION session keys. Stop if the * next one would not be unique or not a session key ID or if * it would expire before the next poll. The private value * included in the hash is zero if broadcast mode, the peer * cookie if client mode or the host cookie if symmetric modes. */ mpoll = 1 << min(peer->ppoll, peer->hpoll); lifetime = min(sys_automax, NTP_MAXSESSION * mpoll); if (peer->hmode == MODE_BROADCAST) cookie = 0; else cookie = peer->pcookie; for (i = 0; i < NTP_MAXSESSION; i++) { peer->keylist[i] = keyid; peer->keynumber = i; keyid = session_key(&dstadr->sin, &peer->srcadr, keyid, cookie, lifetime); lifetime -= mpoll; if (auth_havekey(keyid) || keyid <= NTP_MAXKEY || lifetime <= mpoll) break; } /* * Save the last session key ID, sequence number and timestamp, * then sign these values for later retrieval by the clients. Be * careful not to use invalid key media. Use the public values * timestamp as filestamp. */ vp = &peer->sndval; if (vp->ptr == NULL) vp->ptr = emalloc(sizeof(struct autokey)); ap = (struct autokey *)vp->ptr; ap->seq = htonl(peer->keynumber); ap->key = htonl(keyid); vp->tstamp = htonl(tstamp); vp->fstamp = hostval.tstamp; vp->vallen = htonl(sizeof(struct autokey)); vp->siglen = 0; if (tstamp != 0) { if (tstamp < cinfo->first || tstamp > cinfo->last) return (XEVNT_PER); if (vp->sig == NULL) vp->sig = emalloc(sign_siglen); EVP_SignInit(&ctx, sign_digest); EVP_SignUpdate(&ctx, (u_char *)vp, 12); EVP_SignUpdate(&ctx, vp->ptr, sizeof(struct autokey)); if (EVP_SignFinal(&ctx, vp->sig, &len, sign_pkey)) vp->siglen = htonl(len); else msyslog(LOG_ERR, "make_keys %s\n", ERR_error_string(ERR_get_error(), NULL)); peer->flags |= FLAG_ASSOC; } #ifdef DEBUG if (debug) printf("make_keys: %d %08x %08x ts %u fs %u poll %d\n", ntohl(ap->seq), ntohl(ap->key), cookie, ntohl(vp->tstamp), ntohl(vp->fstamp), peer->hpoll); #endif return (XEVNT_OK); } /* * crypto_recv - parse extension fields * * This routine is called when the packet has been matched to an * association and passed sanity, format and MAC checks. We believe the * extension field values only if the field has proper format and * length, the timestamp and filestamp are valid and the signature has * valid length and is verified. There are a few cases where some values * are believed even if the signature fails, but only if the proventic * bit is not set. */ int crypto_recv( struct peer *peer, /* peer structure pointer */ struct recvbuf *rbufp /* packet buffer pointer */ ) { const EVP_MD *dp; /* message digest algorithm */ u_int32 *pkt; /* receive packet pointer */ struct autokey *ap, *bp; /* autokey pointer */ struct exten *ep, *fp; /* extension pointers */ int has_mac; /* length of MAC field */ int authlen; /* offset of MAC field */ associd_t associd; /* association ID */ tstamp_t tstamp = 0; /* timestamp */ tstamp_t fstamp = 0; /* filestamp */ u_int len; /* extension field length */ u_int code; /* extension field opcode */ u_int vallen = 0; /* value length */ X509 *cert; /* X509 certificate */ char statstr[NTP_MAXSTRLEN]; /* statistics for filegen */ keyid_t cookie; /* crumbles */ int hismode; /* packet mode */ int rval = XEVNT_OK; u_char *ptr; u_int32 temp32; /* * Initialize. Note that the packet has already been checked for * valid format and extension field lengths. First extract the * field length, command code and association ID in host byte * order. These are used with all commands and modes. Then check * the version number, which must be 2, and length, which must * be at least 8 for requests and VALUE_LEN (24) for responses. * Packets that fail either test sink without a trace. The * association ID is saved only if nonzero. */ authlen = LEN_PKT_NOMAC; hismode = (int)PKT_MODE((&rbufp->recv_pkt)->li_vn_mode); while ((has_mac = rbufp->recv_length - authlen) > MAX_MAC_LEN) { pkt = (u_int32 *)&rbufp->recv_pkt + authlen / 4; ep = (struct exten *)pkt; code = ntohl(ep->opcode) & 0xffff0000; len = ntohl(ep->opcode) & 0x0000ffff; associd = (associd_t) ntohl(pkt[1]); rval = XEVNT_OK; #ifdef DEBUG if (debug) printf( "crypto_recv: flags 0x%x ext offset %d len %u code 0x%x assocID %d\n", peer->crypto, authlen, len, code >> 16, associd); #endif /* * Check version number and field length. If bad, * quietly ignore the packet. */ if (((code >> 24) & 0x3f) != CRYPTO_VN || len < 8) { sys_unknownversion++; code |= CRYPTO_ERROR; } /* * Little vulnerability bandage here. If a perp tosses a * fake association ID over the fence, we better toss it * out. Only the first one counts. */ if (code & CRYPTO_RESP) { if (peer->assoc == 0) peer->assoc = associd; else if (peer->assoc != associd) code |= CRYPTO_ERROR; } if (len >= VALUE_LEN) { tstamp = ntohl(ep->tstamp); fstamp = ntohl(ep->fstamp); vallen = ntohl(ep->vallen); } switch (code) { /* * Install status word, host name, signature scheme and * association ID. In OpenSSL the signature algorithm is * bound to the digest algorithm, so the NID completely * defines the signature scheme. Note the request and * response are identical, but neither is validated by * signature. The request is processed here only in * symmetric modes. The server name field might be * useful to implement access controls in future. */ case CRYPTO_ASSOC: /* * If the machine is running when this message * arrives, the other fellow has reset and so * must we. Otherwise, pass the extension field * to the transmit side. */ if (peer->crypto) { rval = XEVNT_ERR; break; } fp = emalloc(len); memcpy(fp, ep, len); temp32 = CRYPTO_RESP; fp->opcode |= htonl(temp32); peer->cmmd = fp; /* fall through */ case CRYPTO_ASSOC | CRYPTO_RESP: /* * Discard the message if it has already been * stored or the message has been amputated. */ if (peer->crypto) break; if (vallen == 0 || vallen > MAXHOSTNAME || len < VALUE_LEN + vallen) { rval = XEVNT_LEN; break; } /* * Check the identity schemes are compatible. If * the client has PC, the server must have PC, * in which case the server public key and * identity are presumed valid, so we skip the * certificate and identity exchanges and move * immediately to the cookie exchange which * confirms the server signature. */ #ifdef DEBUG if (debug) printf( "crypto_recv: ident host 0x%x server 0x%x\n", crypto_flags, fstamp); #endif temp32 = (crypto_flags | ident_scheme) & fstamp & CRYPTO_FLAG_MASK; if (crypto_flags & CRYPTO_FLAG_PRIV) { if (!(fstamp & CRYPTO_FLAG_PRIV)) { rval = XEVNT_KEY; break; } else { fstamp |= CRYPTO_FLAG_VALID | CRYPTO_FLAG_VRFY | CRYPTO_FLAG_SIGN; } /* * In symmetric modes it is an error if either * peer requests identity and the other peer * does not support it. */ } else if ((hismode == MODE_ACTIVE || hismode == MODE_PASSIVE) && ((crypto_flags | fstamp) & CRYPTO_FLAG_MASK) && !temp32) { rval = XEVNT_KEY; break; /* * It is an error if the client requests * identity and the server does not support it. */ } else if (hismode == MODE_CLIENT && (fstamp & CRYPTO_FLAG_MASK) && !temp32) { rval = XEVNT_KEY; break; } /* * Otherwise, the identity scheme(s) are those * that both client and server support. */ fstamp = temp32 | (fstamp & ~CRYPTO_FLAG_MASK); /* * Discard the message if the signature digest * NID is not supported. */ temp32 = (fstamp >> 16) & 0xffff; dp = (const EVP_MD *)EVP_get_digestbynid(temp32); if (dp == NULL) { rval = XEVNT_MD; break; } /* * Save status word, host name and message * digest/signature type. */ peer->crypto = fstamp; peer->digest = dp; peer->subject = emalloc(vallen + 1); memcpy(peer->subject, ep->pkt, vallen); peer->subject[vallen] = '\0'; peer->issuer = emalloc(vallen + 1); strcpy(peer->issuer, peer->subject); temp32 = (fstamp >> 16) & 0xffff; snprintf(statstr, NTP_MAXSTRLEN, "flags 0x%x host %s signature %s", fstamp, peer->subject, OBJ_nid2ln(temp32)); record_crypto_stats(&peer->srcadr, statstr); #ifdef DEBUG if (debug) printf("crypto_recv: %s\n", statstr); #endif break; /* * Decode X509 certificate in ASN.1 format and extract * the data containing, among other things, subject * name and public key. In the default identification * scheme, the certificate trail is followed to a self * signed trusted certificate. */ case CRYPTO_CERT | CRYPTO_RESP: /* * Discard the message if invalid. */ if ((rval = crypto_verify(ep, NULL, peer)) != XEVNT_OK) break; /* * Scan the certificate list to delete old * versions and link the newest version first on * the list. */ if ((rval = cert_install(ep, peer)) != XEVNT_OK) break; /* * If we snatch the certificate before the * server certificate has been signed by its * server, it will be self signed. When it is, * we chase the certificate issuer, which the * server has, and keep going until a self * signed trusted certificate is found. Be sure * to update the issuer field, since it may * change. */ if (peer->issuer != NULL) free(peer->issuer); peer->issuer = emalloc(strlen(cinfo->issuer) + 1); strcpy(peer->issuer, cinfo->issuer); /* * We plug in the public key and lifetime from * the first certificate received. However, note * that this certificate might not be signed by * the server, so we can't check the * signature/digest NID. */ if (peer->pkey == NULL) { ptr = (u_char *)cinfo->cert.ptr; cert = d2i_X509(NULL, &ptr, ntohl(cinfo->cert.vallen)); peer->pkey = X509_get_pubkey(cert); X509_free(cert); } peer->flash &= ~TEST8; temp32 = cinfo->nid; snprintf(statstr, NTP_MAXSTRLEN, "cert %s 0x%x %s (%u) fs %u", cinfo->subject, cinfo->flags, OBJ_nid2ln(temp32), temp32, ntohl(ep->fstamp)); record_crypto_stats(&peer->srcadr, statstr); #ifdef DEBUG if (debug) printf("crypto_recv: %s\n", statstr); #endif break; /* * Schnorr (IFF)identity scheme. This scheme is designed * for use with shared secret group keys and where the * certificate may be generated by a third party. The * client sends a challenge to the server, which * performs a calculation and returns the result. A * positive result is possible only if both client and * server contain the same secret group key. */ case CRYPTO_IFF | CRYPTO_RESP: /* * Discard the message if invalid or certificate * trail not trusted. */ if (!(peer->crypto & CRYPTO_FLAG_VALID)) { rval = XEVNT_ERR; break; } if ((rval = crypto_verify(ep, NULL, peer)) != XEVNT_OK) break; /* * If the the challenge matches the response, * the certificate public key, as well as the * server public key, signatyre and identity are * all verified at the same time. The server is * declared trusted, so we skip further * certificate stages and move immediately to * the cookie stage. */ if ((rval = crypto_iff(ep, peer)) != XEVNT_OK) break; peer->crypto |= CRYPTO_FLAG_VRFY | CRYPTO_FLAG_PROV; peer->flash &= ~TEST8; snprintf(statstr, NTP_MAXSTRLEN, "iff fs %u", ntohl(ep->fstamp)); record_crypto_stats(&peer->srcadr, statstr); #ifdef DEBUG if (debug) printf("crypto_recv: %s\n", statstr); #endif break; /* * Guillou-Quisquater (GQ) identity scheme. This scheme * is designed for use with public certificates carrying * the GQ public key in an extension field. The client * sends a challenge to the server, which performs a * calculation and returns the result. A positive result * is possible only if both client and server contain * the same group key and the server has the matching GQ * private key. */ case CRYPTO_GQ | CRYPTO_RESP: /* * Discard the message if invalid or certificate * trail not trusted. */ if (!(peer->crypto & CRYPTO_FLAG_VALID)) { rval = XEVNT_ERR; break; } if ((rval = crypto_verify(ep, NULL, peer)) != XEVNT_OK) break; /* * If the the challenge matches the response, * the certificate public key, as well as the * server public key, signatyre and identity are * all verified at the same time. The server is * declared trusted, so we skip further * certificate stages and move immediately to * the cookie stage. */ if ((rval = crypto_gq(ep, peer)) != XEVNT_OK) break; peer->crypto |= CRYPTO_FLAG_VRFY | CRYPTO_FLAG_PROV; peer->flash &= ~TEST8; snprintf(statstr, NTP_MAXSTRLEN, "gq fs %u", ntohl(ep->fstamp)); record_crypto_stats(&peer->srcadr, statstr); #ifdef DEBUG if (debug) printf("crypto_recv: %s\n", statstr); #endif break; /* * MV */ case CRYPTO_MV | CRYPTO_RESP: /* * Discard the message if invalid or certificate * trail not trusted. */ if (!(peer->crypto & CRYPTO_FLAG_VALID)) { rval = XEVNT_ERR; break; } if ((rval = crypto_verify(ep, NULL, peer)) != XEVNT_OK) break; /* * If the the challenge matches the response, * the certificate public key, as well as the * server public key, signatyre and identity are * all verified at the same time. The server is * declared trusted, so we skip further * certificate stages and move immediately to * the cookie stage. */ if ((rval = crypto_mv(ep, peer)) != XEVNT_OK) break; peer->crypto |= CRYPTO_FLAG_VRFY | CRYPTO_FLAG_PROV; peer->flash &= ~TEST8; snprintf(statstr, NTP_MAXSTRLEN, "mv fs %u", ntohl(ep->fstamp)); record_crypto_stats(&peer->srcadr, statstr); #ifdef DEBUG if (debug) printf("crypto_recv: %s\n", statstr); #endif break; /* * Cookie request in symmetric modes. Roll a random * cookie and install in symmetric mode. Encrypt for the * response, which is transmitted later. */ case CRYPTO_COOK: /* * Discard the message if invalid or certificate * trail not trusted. */ if (!(peer->crypto & CRYPTO_FLAG_VALID)) { rval = XEVNT_ERR; break; } if ((rval = crypto_verify(ep, NULL, peer)) != XEVNT_OK) break; /* * Pass the extension field to the transmit * side. If already agreed, walk away. */ fp = emalloc(len); memcpy(fp, ep, len); temp32 = CRYPTO_RESP; fp->opcode |= htonl(temp32); peer->cmmd = fp; if (peer->crypto & CRYPTO_FLAG_AGREE) { peer->flash &= ~TEST8; break; } /* * Install cookie values and light the cookie * bit. The transmit side will pick up and * encrypt it for the response. */ key_expire(peer); peer->cookval.tstamp = ep->tstamp; peer->cookval.fstamp = ep->fstamp; RAND_bytes((u_char *)&peer->pcookie, 4); peer->crypto &= ~CRYPTO_FLAG_AUTO; peer->crypto |= CRYPTO_FLAG_AGREE; peer->flash &= ~TEST8; snprintf(statstr, NTP_MAXSTRLEN, "cook %x ts %u fs %u", peer->pcookie, ntohl(ep->tstamp), ntohl(ep->fstamp)); record_crypto_stats(&peer->srcadr, statstr); #ifdef DEBUG if (debug) printf("crypto_recv: %s\n", statstr); #endif break; /* * Cookie response in client and symmetric modes. If the * cookie bit is set, the working cookie is the EXOR of * the current and new values. */ case CRYPTO_COOK | CRYPTO_RESP: /* * Discard the message if invalid or identity * not confirmed or signature not verified with * respect to the cookie values. */ if (!(peer->crypto & CRYPTO_FLAG_VRFY)) { rval = XEVNT_ERR; break; } if ((rval = crypto_verify(ep, &peer->cookval, peer)) != XEVNT_OK) break; /* * Decrypt the cookie, hunting all the time for * errors. */ if (vallen == (u_int) EVP_PKEY_size(host_pkey)) { - RSA_private_decrypt(vallen, + u_int32 *cookiebuf = malloc( + RSA_size(host_pkey->pkey.rsa)); + if (cookiebuf == NULL) { + rval = XEVNT_CKY; + break; + } + if (RSA_private_decrypt(vallen, (u_char *)ep->pkt, - (u_char *)&temp32, + (u_char *)cookiebuf, host_pkey->pkey.rsa, - RSA_PKCS1_OAEP_PADDING); - cookie = ntohl(temp32); + RSA_PKCS1_OAEP_PADDING) != 4) { + rval = XEVNT_CKY; + free(cookiebuf); + break; + } else { + cookie = ntohl(*cookiebuf); + free(cookiebuf); + } } else { rval = XEVNT_CKY; break; } /* * Install cookie values and light the cookie * bit. If this is not broadcast client mode, we * are done here. */ key_expire(peer); peer->cookval.tstamp = ep->tstamp; peer->cookval.fstamp = ep->fstamp; if (peer->crypto & CRYPTO_FLAG_AGREE) peer->pcookie ^= cookie; else peer->pcookie = cookie; if (peer->hmode == MODE_CLIENT && !(peer->cast_flags & MDF_BCLNT)) peer->crypto |= CRYPTO_FLAG_AUTO; else peer->crypto &= ~CRYPTO_FLAG_AUTO; peer->crypto |= CRYPTO_FLAG_AGREE; peer->flash &= ~TEST8; snprintf(statstr, NTP_MAXSTRLEN, "cook %x ts %u fs %u", peer->pcookie, ntohl(ep->tstamp), ntohl(ep->fstamp)); record_crypto_stats(&peer->srcadr, statstr); #ifdef DEBUG if (debug) printf("crypto_recv: %s\n", statstr); #endif break; /* * Install autokey values in broadcast client and * symmetric modes. We have to do this every time the * sever/peer cookie changes or a new keylist is * rolled. Ordinarily, this is automatic as this message * is piggybacked on the first NTP packet sent upon * either of these events. Note that a broadcast client * or symmetric peer can receive this response without a * matching request. */ case CRYPTO_AUTO | CRYPTO_RESP: /* * Discard the message if invalid or identity * not confirmed or signature not verified with * respect to the receive autokey values. */ if (!(peer->crypto & CRYPTO_FLAG_VRFY)) { rval = XEVNT_ERR; break; } if ((rval = crypto_verify(ep, &peer->recval, peer)) != XEVNT_OK) break; /* * Install autokey values and light the * autokey bit. This is not hard. */ if (peer->recval.ptr == NULL) peer->recval.ptr = emalloc(sizeof(struct autokey)); bp = (struct autokey *)peer->recval.ptr; peer->recval.tstamp = ep->tstamp; peer->recval.fstamp = ep->fstamp; ap = (struct autokey *)ep->pkt; bp->seq = ntohl(ap->seq); bp->key = ntohl(ap->key); peer->pkeyid = bp->key; peer->crypto |= CRYPTO_FLAG_AUTO; peer->flash &= ~TEST8; snprintf(statstr, NTP_MAXSTRLEN, "auto seq %d key %x ts %u fs %u", bp->seq, bp->key, ntohl(ep->tstamp), ntohl(ep->fstamp)); record_crypto_stats(&peer->srcadr, statstr); #ifdef DEBUG if (debug) printf("crypto_recv: %s\n", statstr); #endif break; /* * X509 certificate sign response. Validate the * certificate signed by the server and install. Later * this can be provided to clients of this server in * lieu of the self signed certificate in order to * validate the public key. */ case CRYPTO_SIGN | CRYPTO_RESP: /* * Discard the message if invalid or not * proventic. */ if (!(peer->crypto & CRYPTO_FLAG_PROV)) { rval = XEVNT_ERR; break; } if ((rval = crypto_verify(ep, NULL, peer)) != XEVNT_OK) break; /* * Scan the certificate list to delete old * versions and link the newest version first on * the list. */ if ((rval = cert_install(ep, peer)) != XEVNT_OK) break; peer->crypto |= CRYPTO_FLAG_SIGN; peer->flash &= ~TEST8; temp32 = cinfo->nid; snprintf(statstr, NTP_MAXSTRLEN, "sign %s 0x%x %s (%u) fs %u", cinfo->issuer, cinfo->flags, OBJ_nid2ln(temp32), temp32, ntohl(ep->fstamp)); record_crypto_stats(&peer->srcadr, statstr); #ifdef DEBUG if (debug) printf("crypto_recv: %s\n", statstr); #endif break; /* * Install leapseconds table in symmetric modes. This * table is proventicated to the NIST primary servers, * either by copying the file containing the table from * a NIST server to a trusted server or directly using * this protocol. While the entire table is installed at * the server, presently only the current TAI offset is * provided via the kernel to other applications. */ case CRYPTO_TAI: /* * Discard the message if invalid. */ if ((rval = crypto_verify(ep, NULL, peer)) != XEVNT_OK) break; /* * Pass the extension field to the transmit * side. Continue below if a leapseconds table * accompanies the message. */ fp = emalloc(len); memcpy(fp, ep, len); temp32 = CRYPTO_RESP; fp->opcode |= htonl(temp32); peer->cmmd = fp; if (len <= VALUE_LEN) { peer->flash &= ~TEST8; break; } /* fall through */ case CRYPTO_TAI | CRYPTO_RESP: /* * If this is a response, discard the message if * signature not verified with respect to the * leapsecond table values. */ if (peer->cmmd == NULL) { if ((rval = crypto_verify(ep, &peer->tai_leap, peer)) != XEVNT_OK) break; } /* * Initialize peer variables with latest update. */ peer->tai_leap.tstamp = ep->tstamp; peer->tai_leap.fstamp = ep->fstamp; peer->tai_leap.vallen = ep->vallen; /* * Install the new table if there is no stored * table or the new table is more recent than * the stored table. Since a filestamp may have * changed, recompute the signatures. */ if (ntohl(peer->tai_leap.fstamp) > ntohl(tai_leap.fstamp)) { tai_leap.fstamp = ep->fstamp; tai_leap.vallen = ep->vallen; if (tai_leap.ptr != NULL) free(tai_leap.ptr); tai_leap.ptr = emalloc(vallen); memcpy(tai_leap.ptr, ep->pkt, vallen); crypto_update(); } crypto_flags |= CRYPTO_FLAG_TAI; peer->crypto |= CRYPTO_FLAG_LEAP; peer->flash &= ~TEST8; snprintf(statstr, NTP_MAXSTRLEN, "leap %u ts %u fs %u", vallen, ntohl(ep->tstamp), ntohl(ep->fstamp)); record_crypto_stats(&peer->srcadr, statstr); #ifdef DEBUG if (debug) printf("crypto_recv: %s\n", statstr); #endif break; /* * We come here in symmetric modes for miscellaneous * commands that have value fields but are processed on * the transmit side. All we need do here is check for * valid field length. Remaining checks are below and on * the transmit side. */ case CRYPTO_CERT: case CRYPTO_IFF: case CRYPTO_GQ: case CRYPTO_MV: case CRYPTO_SIGN: if (len < VALUE_LEN) { rval = XEVNT_LEN; break; } /* fall through */ /* * We come here for miscellaneous requests and unknown * requests and responses. If an unknown response or * error, forget it. If a request, save the extension * field for later. Unknown requests will be caught on * the transmit side. */ default: if (code & (CRYPTO_RESP | CRYPTO_ERROR)) { rval = XEVNT_ERR; } else if ((rval = crypto_verify(ep, NULL, peer)) == XEVNT_OK) { fp = emalloc(len); memcpy(fp, ep, len); temp32 = CRYPTO_RESP; fp->opcode |= htonl(temp32); peer->cmmd = fp; } } /* * We don't log length/format/timestamp errors and * duplicates, which are log clogging vulnerabilities. * The first error found terminates the extension field * scan and we return the laundry to the caller. A * length/format/timestamp error on transmit is * cheerfully ignored, as the message is not sent. */ if (rval > XEVNT_TSP) { snprintf(statstr, NTP_MAXSTRLEN, "error %x opcode %x ts %u fs %u", rval, code, tstamp, fstamp); record_crypto_stats(&peer->srcadr, statstr); report_event(rval, peer); #ifdef DEBUG if (debug) printf("crypto_recv: %s\n", statstr); #endif break; } else if (rval > XEVNT_OK && (code & CRYPTO_RESP)) { rval = XEVNT_OK; } authlen += len; } return (rval); } /* * crypto_xmit - construct extension fields * * This routine is called both when an association is configured and * when one is not. The only case where this matters is to retrieve the * autokey information, in which case the caller has to provide the * association ID to match the association. * * Returns length of extension field. */ int crypto_xmit( struct pkt *xpkt, /* transmit packet pointer */ struct sockaddr_storage *srcadr_sin, /* active runway */ int start, /* offset to extension field */ struct exten *ep, /* extension pointer */ keyid_t cookie /* session cookie */ ) { u_int32 *pkt; /* packet pointer */ struct peer *peer; /* peer structure pointer */ u_int opcode; /* extension field opcode */ struct exten *fp; /* extension pointers */ struct cert_info *cp, *xp; /* certificate info/value pointer */ char certname[MAXHOSTNAME + 1]; /* subject name buffer */ char statstr[NTP_MAXSTRLEN]; /* statistics for filegen */ tstamp_t tstamp; u_int vallen; u_int len; struct value vtemp; associd_t associd; int rval; keyid_t tcookie; /* * Generate the requested extension field request code, length * and association ID. If this is a response and the host is not * synchronized, light the error bit and go home. */ pkt = (u_int32 *)xpkt + start / 4; fp = (struct exten *)pkt; opcode = ntohl(ep->opcode); associd = (associd_t) ntohl(ep->associd); fp->associd = htonl(associd); len = 8; rval = XEVNT_OK; tstamp = crypto_time(); switch (opcode & 0xffff0000) { /* * Send association request and response with status word and * host name. Note, this message is not signed and the filestamp * contains only the status word. */ case CRYPTO_ASSOC | CRYPTO_RESP: len += crypto_send(fp, &hostval); fp->fstamp = htonl(crypto_flags); break; case CRYPTO_ASSOC: len += crypto_send(fp, &hostval); fp->fstamp = htonl(crypto_flags | ident_scheme); break; /* * Send certificate request. Use the values from the extension * field. */ case CRYPTO_CERT: memset(&vtemp, 0, sizeof(vtemp)); vtemp.tstamp = ep->tstamp; vtemp.fstamp = ep->fstamp; vtemp.vallen = ep->vallen; vtemp.ptr = (u_char *)ep->pkt; len += crypto_send(fp, &vtemp); break; /* * Send certificate response or sign request. Use the values * from the certificate cache. If the request contains no * subject name, assume the name of this host. This is for * backwards compatibility. Private certificates are never sent. */ case CRYPTO_SIGN: case CRYPTO_CERT | CRYPTO_RESP: vallen = ntohl(ep->vallen); if (vallen == 8) { strcpy(certname, sys_hostname); } else if (vallen == 0 || vallen > MAXHOSTNAME) { rval = XEVNT_LEN; break; } else { memcpy(certname, ep->pkt, vallen); certname[vallen] = '\0'; } /* * Find all certificates with matching subject. If a * self-signed, trusted certificate is found, use that. * If not, use the first one with matching subject. A * private certificate is never divulged or signed. */ xp = NULL; for (cp = cinfo; cp != NULL; cp = cp->link) { if (cp->flags & CERT_PRIV) continue; if (strcmp(certname, cp->subject) == 0) { if (xp == NULL) xp = cp; if (strcmp(certname, cp->issuer) == 0 && cp->flags & CERT_TRUST) { xp = cp; break; } } } /* * Be careful who you trust. If not yet synchronized, * give back an empty response. If certificate not found * or beyond the lifetime, return an error. This is to * avoid a bad dude trying to get an expired certificate * re-signed. Otherwise, send it. * * Note the timestamp and filestamp are taken from the * certificate value structure. For all certificates the * timestamp is the latest signature update time. For * host and imported certificates the filestamp is the * creation epoch. For signed certificates the filestamp * is the creation epoch of the trusted certificate at * the base of the certificate trail. In principle, this * allows strong checking for signature masquerade. */ if (tstamp == 0) break; if (xp == NULL) rval = XEVNT_CRT; else if (tstamp < xp->first || tstamp > xp->last) rval = XEVNT_SRV; else len += crypto_send(fp, &xp->cert); break; /* * Send challenge in Schnorr (IFF) identity scheme. */ case CRYPTO_IFF: if ((peer = findpeerbyassoc(ep->pkt[0])) == NULL) { rval = XEVNT_ERR; break; } if ((rval = crypto_alice(peer, &vtemp)) == XEVNT_OK) { len += crypto_send(fp, &vtemp); value_free(&vtemp); } break; /* * Send response in Schnorr (IFF) identity scheme. */ case CRYPTO_IFF | CRYPTO_RESP: if ((rval = crypto_bob(ep, &vtemp)) == XEVNT_OK) { len += crypto_send(fp, &vtemp); value_free(&vtemp); } break; /* * Send challenge in Guillou-Quisquater (GQ) identity scheme. */ case CRYPTO_GQ: if ((peer = findpeerbyassoc(ep->pkt[0])) == NULL) { rval = XEVNT_ERR; break; } if ((rval = crypto_alice2(peer, &vtemp)) == XEVNT_OK) { len += crypto_send(fp, &vtemp); value_free(&vtemp); } break; /* * Send response in Guillou-Quisquater (GQ) identity scheme. */ case CRYPTO_GQ | CRYPTO_RESP: if ((rval = crypto_bob2(ep, &vtemp)) == XEVNT_OK) { len += crypto_send(fp, &vtemp); value_free(&vtemp); } break; /* * Send challenge in MV identity scheme. */ case CRYPTO_MV: if ((peer = findpeerbyassoc(ep->pkt[0])) == NULL) { rval = XEVNT_ERR; break; } if ((rval = crypto_alice3(peer, &vtemp)) == XEVNT_OK) { len += crypto_send(fp, &vtemp); value_free(&vtemp); } break; /* * Send response in MV identity scheme. */ case CRYPTO_MV | CRYPTO_RESP: if ((rval = crypto_bob3(ep, &vtemp)) == XEVNT_OK) { len += crypto_send(fp, &vtemp); value_free(&vtemp); } break; /* * Send certificate sign response. The integrity of the request * certificate has already been verified on the receive side. * Sign the response using the local server key. Use the * filestamp from the request and use the timestamp as the * current time. Light the error bit if the certificate is * invalid or contains an unverified signature. */ case CRYPTO_SIGN | CRYPTO_RESP: if ((rval = cert_sign(ep, &vtemp)) == XEVNT_OK) len += crypto_send(fp, &vtemp); value_free(&vtemp); break; /* * Send public key and signature. Use the values from the public * key. */ case CRYPTO_COOK: len += crypto_send(fp, &pubkey); break; /* * Encrypt and send cookie and signature. Light the error bit if * anything goes wrong. */ case CRYPTO_COOK | CRYPTO_RESP: if ((opcode & 0xffff) < VALUE_LEN) { rval = XEVNT_LEN; break; } if (PKT_MODE(xpkt->li_vn_mode) == MODE_SERVER) { tcookie = cookie; } else { if ((peer = findpeerbyassoc(associd)) == NULL) { rval = XEVNT_ERR; break; } tcookie = peer->pcookie; } if ((rval = crypto_encrypt(ep, &vtemp, &tcookie)) == XEVNT_OK) len += crypto_send(fp, &vtemp); value_free(&vtemp); break; /* * Find peer and send autokey data and signature in broadcast * server and symmetric modes. Use the values in the autokey * structure. If no association is found, either the server has * restarted with new associations or some perp has replayed an * old message, in which case light the error bit. */ case CRYPTO_AUTO | CRYPTO_RESP: if ((peer = findpeerbyassoc(associd)) == NULL) { rval = XEVNT_ERR; break; } peer->flags &= ~FLAG_ASSOC; len += crypto_send(fp, &peer->sndval); break; /* * Send leapseconds table and signature. Use the values from the * tai structure. If no table has been loaded, just send an * empty request. */ case CRYPTO_TAI: case CRYPTO_TAI | CRYPTO_RESP: if (crypto_flags & CRYPTO_FLAG_TAI) len += crypto_send(fp, &tai_leap); break; /* * Default - Fall through for requests; for unknown responses, * flag as error. */ default: if (opcode & CRYPTO_RESP) rval = XEVNT_ERR; } /* * In case of error, flame the log. If a request, toss the * puppy; if a response, return so the sender can flame, too. */ if (rval != XEVNT_OK) { opcode |= CRYPTO_ERROR; snprintf(statstr, NTP_MAXSTRLEN, "error %x opcode %x", rval, opcode); record_crypto_stats(srcadr_sin, statstr); report_event(rval, NULL); #ifdef DEBUG if (debug) printf("crypto_xmit: %s\n", statstr); #endif if (!(opcode & CRYPTO_RESP)) return (0); } /* * Round up the field length to a multiple of 8 bytes and save * the request code and length. */ len = ((len + 7) / 8) * 8; fp->opcode = htonl((opcode & 0xffff0000) | len); #ifdef DEBUG if (debug) printf( "crypto_xmit: flags 0x%x ext offset %d len %u code 0x%x assocID %d\n", crypto_flags, start, len, opcode >> 16, associd); #endif return (len); } /* * crypto_verify - parse and verify the extension field and value * * Returns * XEVNT_OK success * XEVNT_LEN bad field format or length * XEVNT_TSP bad timestamp * XEVNT_FSP bad filestamp * XEVNT_PUB bad or missing public key * XEVNT_SGL bad signature length * XEVNT_SIG signature not verified * XEVNT_ERR protocol error */ static int crypto_verify( struct exten *ep, /* extension pointer */ struct value *vp, /* value pointer */ struct peer *peer /* peer structure pointer */ ) { EVP_PKEY *pkey; /* server public key */ EVP_MD_CTX ctx; /* signature context */ tstamp_t tstamp, tstamp1 = 0; /* timestamp */ tstamp_t fstamp, fstamp1 = 0; /* filestamp */ u_int vallen; /* value length */ u_int siglen; /* signature length */ u_int opcode, len; int i; /* * We require valid opcode and field lengths, timestamp, * filestamp, public key, digest, signature length and * signature, where relevant. Note that preliminary length * checks are done in the main loop. */ len = ntohl(ep->opcode) & 0x0000ffff; opcode = ntohl(ep->opcode) & 0xffff0000; /* * Check for valid operation code and protocol. The opcode must * not have the error bit set. If a response, it must have a * value header. If a request and does not contain a value * header, no need for further checking. */ if (opcode & CRYPTO_ERROR) return (XEVNT_ERR); if (opcode & CRYPTO_RESP) { if (len < VALUE_LEN) return (XEVNT_LEN); } else { if (len < VALUE_LEN) return (XEVNT_OK); } /* * We have a value header. Check for valid field lengths. The * field length must be long enough to contain the value header, * value and signature. Note both the value and signature fields * are rounded up to the next word. */ vallen = ntohl(ep->vallen); i = (vallen + 3) / 4; siglen = ntohl(ep->pkt[i++]); if (len < VALUE_LEN + ((vallen + 3) / 4) * 4 + ((siglen + 3) / 4) * 4) return (XEVNT_LEN); /* * Punt if this is a response with no data. Punt if this is a * request and a previous response is pending. */ if (opcode & CRYPTO_RESP) { if (vallen == 0) return (XEVNT_LEN); } else { if (peer->cmmd != NULL) return (XEVNT_LEN); } /* * Check for valid timestamp and filestamp. If the timestamp is * zero, the sender is not synchronized and signatures are * disregarded. If not, the timestamp must not precede the * filestamp. The timestamp and filestamp must not precede the * corresponding values in the value structure, if present. Once * the autokey values have been installed, the timestamp must * always be later than the corresponding value in the value * structure. Duplicate timestamps are illegal once the cookie * has been validated. */ tstamp = ntohl(ep->tstamp); fstamp = ntohl(ep->fstamp); if (tstamp == 0) return (XEVNT_OK); if (tstamp < fstamp) return (XEVNT_TSP); if (vp != NULL) { tstamp1 = ntohl(vp->tstamp); fstamp1 = ntohl(vp->fstamp); if ((tstamp < tstamp1 || (tstamp == tstamp1 && (peer->crypto & CRYPTO_FLAG_AUTO)))) return (XEVNT_TSP); if ((tstamp < fstamp1 || fstamp < fstamp1)) return (XEVNT_FSP); } /* * Check for valid signature length, public key and digest * algorithm. */ if (crypto_flags & peer->crypto & CRYPTO_FLAG_PRIV) pkey = sign_pkey; else pkey = peer->pkey; if (siglen == 0 || pkey == NULL || peer->digest == NULL) return (XEVNT_OK); if (siglen != (u_int)EVP_PKEY_size(pkey)) return (XEVNT_SGL); /* * Darn, I thought we would never get here. Verify the * signature. If the identity exchange is verified, light the * proventic bit. If no client identity scheme is specified, * avoid doing the sign exchange. */ EVP_VerifyInit(&ctx, peer->digest); EVP_VerifyUpdate(&ctx, (u_char *)&ep->tstamp, vallen + 12); if (EVP_VerifyFinal(&ctx, (u_char *)&ep->pkt[i], siglen, pkey) <= 0) return (XEVNT_SIG); if (peer->crypto & CRYPTO_FLAG_VRFY) { peer->crypto |= CRYPTO_FLAG_PROV; if (!(crypto_flags & CRYPTO_FLAG_MASK)) peer->crypto |= CRYPTO_FLAG_SIGN; } return (XEVNT_OK); } /* * crypto_encrypt - construct encrypted cookie and signature from * extension field and cookie * * Returns * XEVNT_OK success * XEVNT_PUB bad or missing public key * XEVNT_CKY bad or missing cookie * XEVNT_PER host certificate expired */ static int crypto_encrypt( struct exten *ep, /* extension pointer */ struct value *vp, /* value pointer */ keyid_t *cookie /* server cookie */ ) { EVP_PKEY *pkey; /* public key */ EVP_MD_CTX ctx; /* signature context */ tstamp_t tstamp; /* NTP timestamp */ u_int32 temp32; u_int len; u_char *ptr; /* * Extract the public key from the request. */ len = ntohl(ep->vallen); ptr = (u_char *)ep->pkt; pkey = d2i_PublicKey(EVP_PKEY_RSA, NULL, &ptr, len); if (pkey == NULL) { msyslog(LOG_ERR, "crypto_encrypt %s\n", ERR_error_string(ERR_get_error(), NULL)); return (XEVNT_PUB); } /* * Encrypt the cookie, encode in ASN.1 and sign. */ tstamp = crypto_time(); memset(vp, 0, sizeof(struct value)); vp->tstamp = htonl(tstamp); vp->fstamp = hostval.tstamp; len = EVP_PKEY_size(pkey); vp->vallen = htonl(len); vp->ptr = emalloc(len); temp32 = htonl(*cookie); if (!RSA_public_encrypt(4, (u_char *)&temp32, vp->ptr, pkey->pkey.rsa, RSA_PKCS1_OAEP_PADDING)) { msyslog(LOG_ERR, "crypto_encrypt %s\n", ERR_error_string(ERR_get_error(), NULL)); EVP_PKEY_free(pkey); return (XEVNT_CKY); } EVP_PKEY_free(pkey); vp->siglen = 0; if (tstamp == 0) return (XEVNT_OK); if (tstamp < cinfo->first || tstamp > cinfo->last) return (XEVNT_PER); vp->sig = emalloc(sign_siglen); EVP_SignInit(&ctx, sign_digest); EVP_SignUpdate(&ctx, (u_char *)&vp->tstamp, 12); EVP_SignUpdate(&ctx, vp->ptr, len); if (EVP_SignFinal(&ctx, vp->sig, &len, sign_pkey)) vp->siglen = htonl(len); return (XEVNT_OK); } /* * crypto_ident - construct extension field for identity scheme * * This routine determines which identity scheme is in use and * constructs an extension field for that scheme. */ u_int crypto_ident( struct peer *peer /* peer structure pointer */ ) { char filename[MAXFILENAME + 1]; /* * If the server identity has already been verified, no further * action is necessary. Otherwise, try to load the identity file * of the certificate issuer. If the issuer file is not found, * try the host file. If nothing found, declare a cryptobust. * Note we can't get here unless the trusted certificate has * been found and the CRYPTO_FLAG_VALID bit is set, so the * certificate issuer is valid. */ if (peer->ident_pkey != NULL) EVP_PKEY_free(peer->ident_pkey); if (peer->crypto & CRYPTO_FLAG_GQ) { snprintf(filename, MAXFILENAME, "ntpkey_gq_%s", peer->issuer); peer->ident_pkey = crypto_key(filename, &peer->fstamp); if (peer->ident_pkey != NULL) return (CRYPTO_GQ); snprintf(filename, MAXFILENAME, "ntpkey_gq_%s", sys_hostname); peer->ident_pkey = crypto_key(filename, &peer->fstamp); if (peer->ident_pkey != NULL) return (CRYPTO_GQ); } if (peer->crypto & CRYPTO_FLAG_IFF) { snprintf(filename, MAXFILENAME, "ntpkey_iff_%s", peer->issuer); peer->ident_pkey = crypto_key(filename, &peer->fstamp); if (peer->ident_pkey != NULL) return (CRYPTO_IFF); snprintf(filename, MAXFILENAME, "ntpkey_iff_%s", sys_hostname); peer->ident_pkey = crypto_key(filename, &peer->fstamp); if (peer->ident_pkey != NULL) return (CRYPTO_IFF); } if (peer->crypto & CRYPTO_FLAG_MV) { snprintf(filename, MAXFILENAME, "ntpkey_mv_%s", peer->issuer); peer->ident_pkey = crypto_key(filename, &peer->fstamp); if (peer->ident_pkey != NULL) return (CRYPTO_MV); snprintf(filename, MAXFILENAME, "ntpkey_mv_%s", sys_hostname); peer->ident_pkey = crypto_key(filename, &peer->fstamp); if (peer->ident_pkey != NULL) return (CRYPTO_MV); } /* * No compatible identity scheme is available. Life is hard. */ msyslog(LOG_INFO, "crypto_ident: no compatible identity scheme found"); return (0); } /* * crypto_args - construct extension field from arguments * * This routine creates an extension field with current timestamps and * specified opcode, association ID and optional string. Note that the * extension field is created here, but freed after the crypto_xmit() * call in the protocol module. * * Returns extension field pointer (no errors). */ struct exten * crypto_args( struct peer *peer, /* peer structure pointer */ u_int opcode, /* operation code */ char *str /* argument string */ ) { tstamp_t tstamp; /* NTP timestamp */ struct exten *ep; /* extension field pointer */ u_int len; /* extension field length */ tstamp = crypto_time(); len = sizeof(struct exten); if (str != NULL) len += strlen(str); ep = emalloc(len); memset(ep, 0, len); if (opcode == 0) return (ep); ep->opcode = htonl(opcode + len); /* * If a response, send our ID; if a request, send the * responder's ID. */ if (opcode & CRYPTO_RESP) ep->associd = htonl(peer->associd); else ep->associd = htonl(peer->assoc); ep->tstamp = htonl(tstamp); ep->fstamp = hostval.tstamp; ep->vallen = 0; if (str != NULL) { ep->vallen = htonl(strlen(str)); memcpy((char *)ep->pkt, str, strlen(str)); } else { ep->pkt[0] = peer->associd; } return (ep); } /* * crypto_send - construct extension field from value components * * Returns extension field length. Note: it is not polite to send a * nonempty signature with zero timestamp or a nonzero timestamp with * empty signature, but these rules are not enforced here. */ u_int crypto_send( struct exten *ep, /* extension field pointer */ struct value *vp /* value pointer */ ) { u_int len, temp32; int i; /* * Copy data. If the data field is empty or zero length, encode * an empty value with length zero. */ ep->tstamp = vp->tstamp; ep->fstamp = vp->fstamp; ep->vallen = vp->vallen; len = 12; temp32 = ntohl(vp->vallen); if (temp32 > 0 && vp->ptr != NULL) memcpy(ep->pkt, vp->ptr, temp32); /* * Copy signature. If the signature field is empty or zero * length, encode an empty signature with length zero. */ i = (temp32 + 3) / 4; len += i * 4 + 4; ep->pkt[i++] = vp->siglen; temp32 = ntohl(vp->siglen); if (temp32 > 0 && vp->sig != NULL) memcpy(&ep->pkt[i], vp->sig, temp32); len += temp32; return (len); } /* * crypto_update - compute new public value and sign extension fields * * This routine runs periodically, like once a day, and when something * changes. It updates the timestamps on three value structures and one * value structure list, then signs all the structures: * * hostval host name (not signed) * pubkey public key * cinfo certificate info/value list * tai_leap leapseconds file * * Filestamps are proventicated data, so this routine is run only when * the host has been synchronized to a proventicated source. Thus, the * timestamp is proventicated, too, and can be used to deflect * clogging attacks and even cook breakfast. * * Returns void (no errors) */ void crypto_update(void) { EVP_MD_CTX ctx; /* message digest context */ struct cert_info *cp, *cpn; /* certificate info/value */ char statstr[NTP_MAXSTRLEN]; /* statistics for filegen */ tstamp_t tstamp; /* NTP timestamp */ u_int len; if ((tstamp = crypto_time()) == 0) return; hostval.tstamp = htonl(tstamp); /* * Sign public key and timestamps. The filestamp is derived from * the host key file extension from wherever the file was * generated. */ if (pubkey.vallen != 0) { pubkey.tstamp = hostval.tstamp; pubkey.siglen = 0; if (pubkey.sig == NULL) pubkey.sig = emalloc(sign_siglen); EVP_SignInit(&ctx, sign_digest); EVP_SignUpdate(&ctx, (u_char *)&pubkey, 12); EVP_SignUpdate(&ctx, pubkey.ptr, ntohl(pubkey.vallen)); if (EVP_SignFinal(&ctx, pubkey.sig, &len, sign_pkey)) pubkey.siglen = htonl(len); } /* * Sign certificates and timestamps. The filestamp is derived * from the certificate file extension from wherever the file * was generated. Note we do not throw expired certificates * away; they may have signed younger ones. */ for (cp = cinfo; cp != NULL; cp = cpn) { cpn = cp->link; cp->cert.tstamp = hostval.tstamp; cp->cert.siglen = 0; if (cp->cert.sig == NULL) cp->cert.sig = emalloc(sign_siglen); EVP_SignInit(&ctx, sign_digest); EVP_SignUpdate(&ctx, (u_char *)&cp->cert, 12); EVP_SignUpdate(&ctx, cp->cert.ptr, ntohl(cp->cert.vallen)); if (EVP_SignFinal(&ctx, cp->cert.sig, &len, sign_pkey)) cp->cert.siglen = htonl(len); } /* * Sign leapseconds table and timestamps. The filestamp is * derived from the leapsecond file extension from wherever the * file was generated. */ if (tai_leap.vallen != 0) { tai_leap.tstamp = hostval.tstamp; tai_leap.siglen = 0; if (tai_leap.sig == NULL) tai_leap.sig = emalloc(sign_siglen); EVP_SignInit(&ctx, sign_digest); EVP_SignUpdate(&ctx, (u_char *)&tai_leap, 12); EVP_SignUpdate(&ctx, tai_leap.ptr, ntohl(tai_leap.vallen)); if (EVP_SignFinal(&ctx, tai_leap.sig, &len, sign_pkey)) tai_leap.siglen = htonl(len); } snprintf(statstr, NTP_MAXSTRLEN, "update ts %u", ntohl(hostval.tstamp)); record_crypto_stats(NULL, statstr); #ifdef DEBUG if (debug) printf("crypto_update: %s\n", statstr); #endif } /* * value_free - free value structure components. * * Returns void (no errors) */ void value_free( struct value *vp /* value structure */ ) { if (vp->ptr != NULL) free(vp->ptr); if (vp->sig != NULL) free(vp->sig); memset(vp, 0, sizeof(struct value)); } /* * crypto_time - returns current NTP time in seconds. */ tstamp_t crypto_time() { l_fp tstamp; /* NTP time */ L_CLR(&tstamp); L_CLR(&tstamp); if (sys_leap != LEAP_NOTINSYNC) get_systime(&tstamp); return (tstamp.l_ui); } /* * asn2ntp - convert ASN1_TIME time structure to NTP time in seconds. */ u_long asn2ntp ( ASN1_TIME *asn1time /* pointer to ASN1_TIME structure */ ) { char *v; /* pointer to ASN1_TIME string */ struct tm tm; /* used to convert to NTP time */ /* * Extract time string YYMMDDHHMMSSZ from ASN1 time structure. * Note that the YY, MM, DD fields start with one, the HH, MM, * SS fiels start with zero and the Z character should be 'Z' * for UTC. Also note that years less than 50 map to years * greater than 100. Dontcha love ASN.1? Better than MIL-188. */ if (asn1time->length > 13) return ((u_long)(~0)); /* We can't use -1 here. It's invalid */ v = (char *)asn1time->data; tm.tm_year = (v[0] - '0') * 10 + v[1] - '0'; if (tm.tm_year < 50) tm.tm_year += 100; tm.tm_mon = (v[2] - '0') * 10 + v[3] - '0' - 1; tm.tm_mday = (v[4] - '0') * 10 + v[5] - '0'; tm.tm_hour = (v[6] - '0') * 10 + v[7] - '0'; tm.tm_min = (v[8] - '0') * 10 + v[9] - '0'; tm.tm_sec = (v[10] - '0') * 10 + v[11] - '0'; tm.tm_wday = 0; tm.tm_yday = 0; tm.tm_isdst = 0; return (timegm(&tm) + JAN_1970); } /* * bigdig() - compute a BIGNUM MD5 hash of a BIGNUM number. */ static int bighash( BIGNUM *bn, /* BIGNUM * from */ BIGNUM *bk /* BIGNUM * to */ ) { EVP_MD_CTX ctx; /* message digest context */ u_char dgst[EVP_MAX_MD_SIZE]; /* message digest */ u_char *ptr; /* a BIGNUM as binary string */ u_int len; len = BN_num_bytes(bn); ptr = emalloc(len); BN_bn2bin(bn, ptr); EVP_DigestInit(&ctx, EVP_md5()); EVP_DigestUpdate(&ctx, ptr, len); EVP_DigestFinal(&ctx, dgst, &len); BN_bin2bn(dgst, len, bk); /* XXX MEMLEAK? free ptr? */ return (1); } /* *********************************************************************** * * * The following routines implement the Schnorr (IFF) identity scheme * * * *********************************************************************** * * The Schnorr (IFF) identity scheme is intended for use when * the ntp-genkeys program does not generate the certificates used in * the protocol and the group key cannot be conveyed in the certificate * itself. For this purpose, new generations of IFF values must be * securely transmitted to all members of the group before use. The * scheme is self contained and independent of new generations of host * keys, sign keys and certificates. * * The IFF identity scheme is based on DSA cryptography and algorithms * described in Stinson p. 285. The IFF values hide in a DSA cuckoo * structure, but only the primes and generator are used. The p is a * 512-bit prime, q a 160-bit prime that divides p - 1 and is a qth root * of 1 mod p; that is, g^q = 1 mod p. The TA rolls primvate random * group key b disguised as a DSA structure member, then computes public * key g^(q - b). These values are shared only among group members and * never revealed in messages. Alice challenges Bob to confirm identity * using the protocol described below. * * How it works * * The scheme goes like this. Both Alice and Bob have the public primes * p, q and generator g. The TA gives private key b to Bob and public * key v = g^(q - a) mod p to Alice. * * Alice rolls new random challenge r and sends to Bob in the IFF * request message. Bob rolls new random k, then computes y = k + b r * mod q and x = g^k mod p and sends (y, hash(x)) to Alice in the * response message. Besides making the response shorter, the hash makes * it effectivey impossible for an intruder to solve for b by observing * a number of these messages. * * Alice receives the response and computes g^y v^r mod p. After a bit * of algebra, this simplifies to g^k. If the hash of this result * matches hash(x), Alice knows that Bob has the group key b. The signed * response binds this knowledge to Bob's private key and the public key * previously received in his certificate. * * crypto_alice - construct Alice's challenge in IFF scheme * * Returns * XEVNT_OK success * XEVNT_PUB bad or missing public key * XEVNT_ID bad or missing group key */ static int crypto_alice( struct peer *peer, /* peer pointer */ struct value *vp /* value pointer */ ) { DSA *dsa; /* IFF parameters */ BN_CTX *bctx; /* BIGNUM context */ EVP_MD_CTX ctx; /* signature context */ tstamp_t tstamp; u_int len; /* * The identity parameters must have correct format and content. */ if (peer->ident_pkey == NULL) return (XEVNT_ID); if ((dsa = peer->ident_pkey->pkey.dsa) == NULL) { msyslog(LOG_INFO, "crypto_alice: defective key"); return (XEVNT_PUB); } /* * Roll new random r (0 < r < q). The OpenSSL library has a bug * omitting BN_rand_range, so we have to do it the hard way. */ bctx = BN_CTX_new(); len = BN_num_bytes(dsa->q); if (peer->iffval != NULL) BN_free(peer->iffval); peer->iffval = BN_new(); BN_rand(peer->iffval, len * 8, -1, 1); /* r */ BN_mod(peer->iffval, peer->iffval, dsa->q, bctx); BN_CTX_free(bctx); /* * Sign and send to Bob. The filestamp is from the local file. */ tstamp = crypto_time(); memset(vp, 0, sizeof(struct value)); vp->tstamp = htonl(tstamp); vp->fstamp = htonl(peer->fstamp); vp->vallen = htonl(len); vp->ptr = emalloc(len); BN_bn2bin(peer->iffval, vp->ptr); vp->siglen = 0; if (tstamp == 0) return (XEVNT_OK); if (tstamp < cinfo->first || tstamp > cinfo->last) return (XEVNT_PER); vp->sig = emalloc(sign_siglen); EVP_SignInit(&ctx, sign_digest); EVP_SignUpdate(&ctx, (u_char *)&vp->tstamp, 12); EVP_SignUpdate(&ctx, vp->ptr, len); if (EVP_SignFinal(&ctx, vp->sig, &len, sign_pkey)) vp->siglen = htonl(len); return (XEVNT_OK); } /* * crypto_bob - construct Bob's response to Alice's challenge * * Returns * XEVNT_OK success * XEVNT_ID bad or missing group key * XEVNT_ERR protocol error * XEVNT_PER host expired certificate */ static int crypto_bob( struct exten *ep, /* extension pointer */ struct value *vp /* value pointer */ ) { DSA *dsa; /* IFF parameters */ DSA_SIG *sdsa; /* DSA signature context fake */ BN_CTX *bctx; /* BIGNUM context */ EVP_MD_CTX ctx; /* signature context */ tstamp_t tstamp; /* NTP timestamp */ BIGNUM *bn, *bk, *r; u_char *ptr; u_int len; /* * If the IFF parameters are not valid, something awful * happened or we are being tormented. */ if (iffpar_pkey == NULL) { msyslog(LOG_INFO, "crypto_bob: scheme unavailable"); return (XEVNT_ID); } dsa = iffpar_pkey->pkey.dsa; /* * Extract r from the challenge. */ len = ntohl(ep->vallen); if ((r = BN_bin2bn((u_char *)ep->pkt, len, NULL)) == NULL) { msyslog(LOG_ERR, "crypto_bob %s\n", ERR_error_string(ERR_get_error(), NULL)); return (XEVNT_ERR); } /* * Bob rolls random k (0 < k < q), computes y = k + b r mod q * and x = g^k mod p, then sends (y, hash(x)) to Alice. */ bctx = BN_CTX_new(); bk = BN_new(); bn = BN_new(); sdsa = DSA_SIG_new(); BN_rand(bk, len * 8, -1, 1); /* k */ BN_mod_mul(bn, dsa->priv_key, r, dsa->q, bctx); /* b r mod q */ BN_add(bn, bn, bk); BN_mod(bn, bn, dsa->q, bctx); /* k + b r mod q */ sdsa->r = BN_dup(bn); BN_mod_exp(bk, dsa->g, bk, dsa->p, bctx); /* g^k mod p */ bighash(bk, bk); sdsa->s = BN_dup(bk); BN_CTX_free(bctx); BN_free(r); BN_free(bn); BN_free(bk); /* * Encode the values in ASN.1 and sign. */ tstamp = crypto_time(); memset(vp, 0, sizeof(struct value)); vp->tstamp = htonl(tstamp); vp->fstamp = htonl(if_fstamp); len = i2d_DSA_SIG(sdsa, NULL); if (len <= 0) { msyslog(LOG_ERR, "crypto_bob %s\n", ERR_error_string(ERR_get_error(), NULL)); DSA_SIG_free(sdsa); return (XEVNT_ERR); } vp->vallen = htonl(len); ptr = emalloc(len); vp->ptr = ptr; i2d_DSA_SIG(sdsa, &ptr); DSA_SIG_free(sdsa); vp->siglen = 0; if (tstamp == 0) return (XEVNT_OK); if (tstamp < cinfo->first || tstamp > cinfo->last) return (XEVNT_PER); vp->sig = emalloc(sign_siglen); EVP_SignInit(&ctx, sign_digest); EVP_SignUpdate(&ctx, (u_char *)&vp->tstamp, 12); EVP_SignUpdate(&ctx, vp->ptr, len); if (EVP_SignFinal(&ctx, vp->sig, &len, sign_pkey)) vp->siglen = htonl(len); return (XEVNT_OK); } /* * crypto_iff - verify Bob's response to Alice's challenge * * Returns * XEVNT_OK success * XEVNT_PUB bad or missing public key * XEVNT_ID bad or missing group key * XEVNT_FSP bad filestamp */ int crypto_iff( struct exten *ep, /* extension pointer */ struct peer *peer /* peer structure pointer */ ) { DSA *dsa; /* IFF parameters */ BN_CTX *bctx; /* BIGNUM context */ DSA_SIG *sdsa; /* DSA parameters */ BIGNUM *bn, *bk; u_int len; const u_char *ptr; int temp; /* * If the IFF parameters are not valid or no challenge was sent, * something awful happened or we are being tormented. */ if (peer->ident_pkey == NULL) { msyslog(LOG_INFO, "crypto_iff: scheme unavailable"); return (XEVNT_ID); } if (ntohl(ep->fstamp) != peer->fstamp) { msyslog(LOG_INFO, "crypto_iff: invalid filestamp %u", ntohl(ep->fstamp)); return (XEVNT_FSP); } if ((dsa = peer->ident_pkey->pkey.dsa) == NULL) { msyslog(LOG_INFO, "crypto_iff: defective key"); return (XEVNT_PUB); } if (peer->iffval == NULL) { msyslog(LOG_INFO, "crypto_iff: missing challenge"); return (XEVNT_ID); } /* * Extract the k + b r and g^k values from the response. */ bctx = BN_CTX_new(); bk = BN_new(); bn = BN_new(); len = ntohl(ep->vallen); ptr = (const u_char *)ep->pkt; if ((sdsa = d2i_DSA_SIG(NULL, &ptr, len)) == NULL) { msyslog(LOG_ERR, "crypto_iff %s\n", ERR_error_string(ERR_get_error(), NULL)); return (XEVNT_ERR); } /* * Compute g^(k + b r) g^(q - b)r mod p. */ BN_mod_exp(bn, dsa->pub_key, peer->iffval, dsa->p, bctx); BN_mod_exp(bk, dsa->g, sdsa->r, dsa->p, bctx); BN_mod_mul(bn, bn, bk, dsa->p, bctx); /* * Verify the hash of the result matches hash(x). */ bighash(bn, bn); temp = BN_cmp(bn, sdsa->s); BN_free(bn); BN_free(bk); BN_CTX_free(bctx); BN_free(peer->iffval); peer->iffval = NULL; DSA_SIG_free(sdsa); if (temp == 0) return (XEVNT_OK); else return (XEVNT_ID); } /* *********************************************************************** * * * The following routines implement the Guillou-Quisquater (GQ) * * identity scheme * * * *********************************************************************** * * The Guillou-Quisquater (GQ) identity scheme is intended for use when * the ntp-genkeys program generates the certificates used in the * protocol and the group key can be conveyed in a certificate extension * field. The scheme is self contained and independent of new * generations of host keys, sign keys and certificates. * * The GQ identity scheme is based on RSA cryptography and algorithms * described in Stinson p. 300 (with errors). The GQ values hide in a * RSA cuckoo structure, but only the modulus is used. The 512-bit * public modulus is n = p q, where p and q are secret large primes. The * TA rolls random group key b disguised as a RSA structure member. * Except for the public key, these values are shared only among group * members and never revealed in messages. * * When rolling new certificates, Bob recomputes the private and * public keys. The private key u is a random roll, while the public key * is the inverse obscured by the group key v = (u^-1)^b. These values * replace the private and public keys normally generated by the RSA * scheme. Alice challenges Bob to confirm identity using the protocol * described below. * * How it works * * The scheme goes like this. Both Alice and Bob have the same modulus n * and some random b as the group key. These values are computed and * distributed in advance via secret means, although only the group key * b is truly secret. Each has a private random private key u and public * key (u^-1)^b, although not necessarily the same ones. Bob and Alice * can regenerate the key pair from time to time without affecting * operations. The public key is conveyed on the certificate in an * extension field; the private key is never revealed. * * Alice rolls new random challenge r and sends to Bob in the GQ * request message. Bob rolls new random k, then computes y = k u^r mod * n and x = k^b mod n and sends (y, hash(x)) to Alice in the response * message. Besides making the response shorter, the hash makes it * effectivey impossible for an intruder to solve for b by observing * a number of these messages. * * Alice receives the response and computes y^b v^r mod n. After a bit * of algebra, this simplifies to k^b. If the hash of this result * matches hash(x), Alice knows that Bob has the group key b. The signed * response binds this knowledge to Bob's private key and the public key * previously received in his certificate. * * crypto_alice2 - construct Alice's challenge in GQ scheme * * Returns * XEVNT_OK success * XEVNT_PUB bad or missing public key * XEVNT_ID bad or missing group key * XEVNT_PER host certificate expired */ static int crypto_alice2( struct peer *peer, /* peer pointer */ struct value *vp /* value pointer */ ) { RSA *rsa; /* GQ parameters */ BN_CTX *bctx; /* BIGNUM context */ EVP_MD_CTX ctx; /* signature context */ tstamp_t tstamp; u_int len; /* * The identity parameters must have correct format and content. */ if (peer->ident_pkey == NULL) return (XEVNT_ID); if ((rsa = peer->ident_pkey->pkey.rsa) == NULL) { msyslog(LOG_INFO, "crypto_alice2: defective key"); return (XEVNT_PUB); } /* * Roll new random r (0 < r < n). The OpenSSL library has a bug * omitting BN_rand_range, so we have to do it the hard way. */ bctx = BN_CTX_new(); len = BN_num_bytes(rsa->n); if (peer->iffval != NULL) BN_free(peer->iffval); peer->iffval = BN_new(); BN_rand(peer->iffval, len * 8, -1, 1); /* r mod n */ BN_mod(peer->iffval, peer->iffval, rsa->n, bctx); BN_CTX_free(bctx); /* * Sign and send to Bob. The filestamp is from the local file. */ tstamp = crypto_time(); memset(vp, 0, sizeof(struct value)); vp->tstamp = htonl(tstamp); vp->fstamp = htonl(peer->fstamp); vp->vallen = htonl(len); vp->ptr = emalloc(len); BN_bn2bin(peer->iffval, vp->ptr); vp->siglen = 0; if (tstamp == 0) return (XEVNT_OK); if (tstamp < cinfo->first || tstamp > cinfo->last) return (XEVNT_PER); vp->sig = emalloc(sign_siglen); EVP_SignInit(&ctx, sign_digest); EVP_SignUpdate(&ctx, (u_char *)&vp->tstamp, 12); EVP_SignUpdate(&ctx, vp->ptr, len); if (EVP_SignFinal(&ctx, vp->sig, &len, sign_pkey)) vp->siglen = htonl(len); return (XEVNT_OK); } /* * crypto_bob2 - construct Bob's response to Alice's challenge * * Returns * XEVNT_OK success * XEVNT_ID bad or missing group key * XEVNT_ERR protocol error * XEVNT_PER host certificate expired */ static int crypto_bob2( struct exten *ep, /* extension pointer */ struct value *vp /* value pointer */ ) { RSA *rsa; /* GQ parameters */ DSA_SIG *sdsa; /* DSA parameters */ BN_CTX *bctx; /* BIGNUM context */ EVP_MD_CTX ctx; /* signature context */ tstamp_t tstamp; /* NTP timestamp */ BIGNUM *r, *k, *g, *y; u_char *ptr; u_int len; /* * If the GQ parameters are not valid, something awful * happened or we are being tormented. */ if (gqpar_pkey == NULL) { msyslog(LOG_INFO, "crypto_bob2: scheme unavailable"); return (XEVNT_ID); } rsa = gqpar_pkey->pkey.rsa; /* * Extract r from the challenge. */ len = ntohl(ep->vallen); if ((r = BN_bin2bn((u_char *)ep->pkt, len, NULL)) == NULL) { msyslog(LOG_ERR, "crypto_bob2 %s\n", ERR_error_string(ERR_get_error(), NULL)); return (XEVNT_ERR); } /* * Bob rolls random k (0 < k < n), computes y = k u^r mod n and * x = k^b mod n, then sends (y, hash(x)) to Alice. */ bctx = BN_CTX_new(); k = BN_new(); g = BN_new(); y = BN_new(); sdsa = DSA_SIG_new(); BN_rand(k, len * 8, -1, 1); /* k */ BN_mod(k, k, rsa->n, bctx); BN_mod_exp(y, rsa->p, r, rsa->n, bctx); /* u^r mod n */ BN_mod_mul(y, k, y, rsa->n, bctx); /* k u^r mod n */ sdsa->r = BN_dup(y); BN_mod_exp(g, k, rsa->e, rsa->n, bctx); /* k^b mod n */ bighash(g, g); sdsa->s = BN_dup(g); BN_CTX_free(bctx); BN_free(r); BN_free(k); BN_free(g); BN_free(y); /* * Encode the values in ASN.1 and sign. */ tstamp = crypto_time(); memset(vp, 0, sizeof(struct value)); vp->tstamp = htonl(tstamp); vp->fstamp = htonl(gq_fstamp); len = i2d_DSA_SIG(sdsa, NULL); if (len <= 0) { msyslog(LOG_ERR, "crypto_bob2 %s\n", ERR_error_string(ERR_get_error(), NULL)); DSA_SIG_free(sdsa); return (XEVNT_ERR); } vp->vallen = htonl(len); ptr = emalloc(len); vp->ptr = ptr; i2d_DSA_SIG(sdsa, &ptr); DSA_SIG_free(sdsa); vp->siglen = 0; if (tstamp == 0) return (XEVNT_OK); if (tstamp < cinfo->first || tstamp > cinfo->last) return (XEVNT_PER); vp->sig = emalloc(sign_siglen); EVP_SignInit(&ctx, sign_digest); EVP_SignUpdate(&ctx, (u_char *)&vp->tstamp, 12); EVP_SignUpdate(&ctx, vp->ptr, len); if (EVP_SignFinal(&ctx, vp->sig, &len, sign_pkey)) vp->siglen = htonl(len); return (XEVNT_OK); } /* * crypto_gq - verify Bob's response to Alice's challenge * * Returns * XEVNT_OK success * XEVNT_PUB bad or missing public key * XEVNT_ID bad or missing group keys * XEVNT_ERR protocol error * XEVNT_FSP bad filestamp */ int crypto_gq( struct exten *ep, /* extension pointer */ struct peer *peer /* peer structure pointer */ ) { RSA *rsa; /* GQ parameters */ BN_CTX *bctx; /* BIGNUM context */ DSA_SIG *sdsa; /* RSA signature context fake */ BIGNUM *y, *v; const u_char *ptr; u_int len; int temp; /* * If the GQ parameters are not valid or no challenge was sent, * something awful happened or we are being tormented. */ if (peer->ident_pkey == NULL) { msyslog(LOG_INFO, "crypto_gq: scheme unavailable"); return (XEVNT_ID); } if (ntohl(ep->fstamp) != peer->fstamp) { msyslog(LOG_INFO, "crypto_gq: invalid filestamp %u", ntohl(ep->fstamp)); return (XEVNT_FSP); } if ((rsa = peer->ident_pkey->pkey.rsa) == NULL) { msyslog(LOG_INFO, "crypto_gq: defective key"); return (XEVNT_PUB); } if (peer->iffval == NULL) { msyslog(LOG_INFO, "crypto_gq: missing challenge"); return (XEVNT_ID); } /* * Extract the y = k u^r and hash(x = k^b) values from the * response. */ bctx = BN_CTX_new(); y = BN_new(); v = BN_new(); len = ntohl(ep->vallen); ptr = (const u_char *)ep->pkt; if ((sdsa = d2i_DSA_SIG(NULL, &ptr, len)) == NULL) { msyslog(LOG_ERR, "crypto_gq %s\n", ERR_error_string(ERR_get_error(), NULL)); return (XEVNT_ERR); } /* * Compute v^r y^b mod n. */ BN_mod_exp(v, peer->grpkey, peer->iffval, rsa->n, bctx); /* v^r mod n */ BN_mod_exp(y, sdsa->r, rsa->e, rsa->n, bctx); /* y^b mod n */ BN_mod_mul(y, v, y, rsa->n, bctx); /* v^r y^b mod n */ /* * Verify the hash of the result matches hash(x). */ bighash(y, y); temp = BN_cmp(y, sdsa->s); BN_CTX_free(bctx); BN_free(y); BN_free(v); BN_free(peer->iffval); peer->iffval = NULL; DSA_SIG_free(sdsa); if (temp == 0) return (XEVNT_OK); else return (XEVNT_ID); } /* *********************************************************************** * * * The following routines implement the Mu-Varadharajan (MV) identity * * scheme * * * *********************************************************************** */ /* * The Mu-Varadharajan (MV) cryptosystem was originally intended when * servers broadcast messages to clients, but clients never send * messages to servers. There is one encryption key for the server and a * separate decryption key for each client. It operated something like a * pay-per-view satellite broadcasting system where the session key is * encrypted by the broadcaster and the decryption keys are held in a * tamperproof set-top box. * * The MV parameters and private encryption key hide in a DSA cuckoo * structure which uses the same parameters, but generated in a * different way. The values are used in an encryption scheme similar to * El Gamal cryptography and a polynomial formed from the expansion of * product terms (x - x[j]), as described in Mu, Y., and V. * Varadharajan: Robust and Secure Broadcasting, Proc. Indocrypt 2001, * 223-231. The paper has significant errors and serious omissions. * * Let q be the product of n distinct primes s'[j] (j = 1...n), where * each s'[j] has m significant bits. Let p be a prime p = 2 * q + 1, so * that q and each s'[j] divide p - 1 and p has M = n * m + 1 * significant bits. The elements x mod q of Zq with the elements 2 and * the primes removed form a field Zq* valid for polynomial arithetic. * Let g be a generator of Zp; that is, gcd(g, p - 1) = 1 and g^q = 1 * mod p. We expect M to be in the 500-bit range and n relatively small, * like 25, so the likelihood of a randomly generated element of x mod q * of Zq colliding with a factor of p - 1 is very small and can be * avoided. Associated with each s'[j] is an element s[j] such that s[j] * s'[j] = s'[j] mod q. We find s[j] as the quotient (q + s'[j]) / * s'[j]. These are the parameters of the scheme and they are expensive * to compute. * * We set up an instance of the scheme as follows. A set of random * values x[j] mod q (j = 1...n), are generated as the zeros of a * polynomial of order n. The product terms (x - x[j]) are expanded to * form coefficients a[i] mod q (i = 0...n) in powers of x. These are * used as exponents of the generator g mod p to generate the private * encryption key A. The pair (gbar, ghat) of public server keys and the * pairs (xbar[j], xhat[j]) (j = 1...n) of private client keys are used * to construct the decryption keys. The devil is in the details. * * The distinguishing characteristic of this scheme is the capability to * revoke keys. Included in the calculation of E, gbar and ghat is the * product s = prod(s'[j]) (j = 1...n) above. If the factor s'[j] is * subsequently removed from the product and E, gbar and ghat * recomputed, the jth client will no longer be able to compute E^-1 and * thus unable to decrypt the block. * * How it works * * The scheme goes like this. Bob has the server values (p, A, q, gbar, * ghat) and Alice the client values (p, xbar, xhat). * * Alice rolls new random challenge r (0 < r < p) and sends to Bob in * the MV request message. Bob rolls new random k (0 < k < q), encrypts * y = A^k mod p (a permutation) and sends (hash(y), gbar^k, ghat^k) to * Alice. * * Alice receives the response and computes the decryption key (the * inverse permutation) from previously obtained (xbar, xhat) and * (gbar^k, ghat^k) in the message. She computes the inverse, which is * unique by reasons explained in the ntp-keygen.c program sources. If * the hash of this result matches hash(y), Alice knows that Bob has the * group key b. The signed response binds this knowledge to Bob's * private key and the public key previously received in his * certificate. * * crypto_alice3 - construct Alice's challenge in MV scheme * * Returns * XEVNT_OK success * XEVNT_PUB bad or missing public key * XEVNT_ID bad or missing group key * XEVNT_PER host certificate expired */ static int crypto_alice3( struct peer *peer, /* peer pointer */ struct value *vp /* value pointer */ ) { DSA *dsa; /* MV parameters */ BN_CTX *bctx; /* BIGNUM context */ EVP_MD_CTX ctx; /* signature context */ tstamp_t tstamp; u_int len; /* * The identity parameters must have correct format and content. */ if (peer->ident_pkey == NULL) return (XEVNT_ID); if ((dsa = peer->ident_pkey->pkey.dsa) == NULL) { msyslog(LOG_INFO, "crypto_alice3: defective key"); return (XEVNT_PUB); } /* * Roll new random r (0 < r < q). The OpenSSL library has a bug * omitting BN_rand_range, so we have to do it the hard way. */ bctx = BN_CTX_new(); len = BN_num_bytes(dsa->p); if (peer->iffval != NULL) BN_free(peer->iffval); peer->iffval = BN_new(); BN_rand(peer->iffval, len * 8, -1, 1); /* r */ BN_mod(peer->iffval, peer->iffval, dsa->p, bctx); BN_CTX_free(bctx); /* * Sign and send to Bob. The filestamp is from the local file. */ tstamp = crypto_time(); memset(vp, 0, sizeof(struct value)); vp->tstamp = htonl(tstamp); vp->fstamp = htonl(peer->fstamp); vp->vallen = htonl(len); vp->ptr = emalloc(len); BN_bn2bin(peer->iffval, vp->ptr); vp->siglen = 0; if (tstamp == 0) return (XEVNT_OK); if (tstamp < cinfo->first || tstamp > cinfo->last) return (XEVNT_PER); vp->sig = emalloc(sign_siglen); EVP_SignInit(&ctx, sign_digest); EVP_SignUpdate(&ctx, (u_char *)&vp->tstamp, 12); EVP_SignUpdate(&ctx, vp->ptr, len); if (EVP_SignFinal(&ctx, vp->sig, &len, sign_pkey)) vp->siglen = htonl(len); return (XEVNT_OK); } /* * crypto_bob3 - construct Bob's response to Alice's challenge * * Returns * XEVNT_OK success * XEVNT_ERR protocol error * XEVNT_PER host certificate expired */ static int crypto_bob3( struct exten *ep, /* extension pointer */ struct value *vp /* value pointer */ ) { DSA *dsa; /* MV parameters */ DSA *sdsa; /* DSA signature context fake */ BN_CTX *bctx; /* BIGNUM context */ EVP_MD_CTX ctx; /* signature context */ tstamp_t tstamp; /* NTP timestamp */ BIGNUM *r, *k, *u; u_char *ptr; u_int len; /* * If the MV parameters are not valid, something awful * happened or we are being tormented. */ if (mvpar_pkey == NULL) { msyslog(LOG_INFO, "crypto_bob3: scheme unavailable"); return (XEVNT_ID); } dsa = mvpar_pkey->pkey.dsa; /* * Extract r from the challenge. */ len = ntohl(ep->vallen); if ((r = BN_bin2bn((u_char *)ep->pkt, len, NULL)) == NULL) { msyslog(LOG_ERR, "crypto_bob3 %s\n", ERR_error_string(ERR_get_error(), NULL)); return (XEVNT_ERR); } /* * Bob rolls random k (0 < k < q), making sure it is not a * factor of q. He then computes y = A^k r and sends (hash(y), * gbar^k, ghat^k) to Alice. */ bctx = BN_CTX_new(); k = BN_new(); u = BN_new(); sdsa = DSA_new(); sdsa->p = BN_new(); sdsa->q = BN_new(); sdsa->g = BN_new(); while (1) { BN_rand(k, BN_num_bits(dsa->q), 0, 0); BN_mod(k, k, dsa->q, bctx); BN_gcd(u, k, dsa->q, bctx); if (BN_is_one(u)) break; } BN_mod_exp(u, dsa->g, k, dsa->p, bctx); /* A r */ BN_mod_mul(u, u, r, dsa->p, bctx); bighash(u, sdsa->p); BN_mod_exp(sdsa->q, dsa->priv_key, k, dsa->p, bctx); /* gbar */ BN_mod_exp(sdsa->g, dsa->pub_key, k, dsa->p, bctx); /* ghat */ BN_CTX_free(bctx); BN_free(k); BN_free(r); BN_free(u); /* * Encode the values in ASN.1 and sign. */ tstamp = crypto_time(); memset(vp, 0, sizeof(struct value)); vp->tstamp = htonl(tstamp); vp->fstamp = htonl(mv_fstamp); len = i2d_DSAparams(sdsa, NULL); if (len <= 0) { msyslog(LOG_ERR, "crypto_bob3 %s\n", ERR_error_string(ERR_get_error(), NULL)); DSA_free(sdsa); return (XEVNT_ERR); } vp->vallen = htonl(len); ptr = emalloc(len); vp->ptr = ptr; i2d_DSAparams(sdsa, &ptr); DSA_free(sdsa); vp->siglen = 0; if (tstamp == 0) return (XEVNT_OK); if (tstamp < cinfo->first || tstamp > cinfo->last) return (XEVNT_PER); vp->sig = emalloc(sign_siglen); EVP_SignInit(&ctx, sign_digest); EVP_SignUpdate(&ctx, (u_char *)&vp->tstamp, 12); EVP_SignUpdate(&ctx, vp->ptr, len); if (EVP_SignFinal(&ctx, vp->sig, &len, sign_pkey)) vp->siglen = htonl(len); return (XEVNT_OK); } /* * crypto_mv - verify Bob's response to Alice's challenge * * Returns * XEVNT_OK success * XEVNT_PUB bad or missing public key * XEVNT_ID bad or missing group key * XEVNT_ERR protocol error * XEVNT_FSP bad filestamp */ int crypto_mv( struct exten *ep, /* extension pointer */ struct peer *peer /* peer structure pointer */ ) { DSA *dsa; /* MV parameters */ DSA *sdsa; /* DSA parameters */ BN_CTX *bctx; /* BIGNUM context */ BIGNUM *k, *u, *v; u_int len; const u_char *ptr; int temp; /* * If the MV parameters are not valid or no challenge was sent, * something awful happened or we are being tormented. */ if (peer->ident_pkey == NULL) { msyslog(LOG_INFO, "crypto_mv: scheme unavailable"); return (XEVNT_ID); } if (ntohl(ep->fstamp) != peer->fstamp) { msyslog(LOG_INFO, "crypto_mv: invalid filestamp %u", ntohl(ep->fstamp)); return (XEVNT_FSP); } if ((dsa = peer->ident_pkey->pkey.dsa) == NULL) { msyslog(LOG_INFO, "crypto_mv: defective key"); return (XEVNT_PUB); } if (peer->iffval == NULL) { msyslog(LOG_INFO, "crypto_mv: missing challenge"); return (XEVNT_ID); } /* * Extract the (hash(y), gbar, ghat) values from the response. */ bctx = BN_CTX_new(); k = BN_new(); u = BN_new(); v = BN_new(); len = ntohl(ep->vallen); ptr = (const u_char *)ep->pkt; if ((sdsa = d2i_DSAparams(NULL, &ptr, len)) == NULL) { msyslog(LOG_ERR, "crypto_mv %s\n", ERR_error_string(ERR_get_error(), NULL)); return (XEVNT_ERR); } /* * Compute (gbar^xhat ghat^xbar)^-1 mod p. */ BN_mod_exp(u, sdsa->q, dsa->pub_key, dsa->p, bctx); BN_mod_exp(v, sdsa->g, dsa->priv_key, dsa->p, bctx); BN_mod_mul(u, u, v, dsa->p, bctx); BN_mod_inverse(u, u, dsa->p, bctx); BN_mod_mul(v, u, peer->iffval, dsa->p, bctx); /* * The result should match the hash of r mod p. */ bighash(v, v); temp = BN_cmp(v, sdsa->p); BN_CTX_free(bctx); BN_free(k); BN_free(u); BN_free(v); BN_free(peer->iffval); peer->iffval = NULL; DSA_free(sdsa); if (temp == 0) return (XEVNT_OK); else return (XEVNT_ID); } /* *********************************************************************** * * * The following routines are used to manipulate certificates * * * *********************************************************************** */ /* * cert_parse - parse x509 certificate and create info/value structures. * * The server certificate includes the version number, issuer name, * subject name, public key and valid date interval. If the issuer name * is the same as the subject name, the certificate is self signed and * valid only if the server is configured as trustable. If the names are * different, another issuer has signed the server certificate and * vouched for it. In this case the server certificate is valid if * verified by the issuer public key. * * Returns certificate info/value pointer if valid, NULL if not. */ struct cert_info * /* certificate information structure */ cert_parse( u_char *asn1cert, /* X509 certificate */ u_int len, /* certificate length */ tstamp_t fstamp /* filestamp */ ) { X509 *cert; /* X509 certificate */ X509_EXTENSION *ext; /* X509v3 extension */ struct cert_info *ret; /* certificate info/value */ BIO *bp; X509V3_EXT_METHOD *method; char pathbuf[MAXFILENAME]; u_char *uptr; char *ptr; int temp, cnt, i; /* * Decode ASN.1 objects and construct certificate structure. */ uptr = asn1cert; if ((cert = d2i_X509(NULL, &uptr, len)) == NULL) { msyslog(LOG_ERR, "cert_parse %s\n", ERR_error_string(ERR_get_error(), NULL)); return (NULL); } /* * Extract version, subject name and public key. */ ret = emalloc(sizeof(struct cert_info)); memset(ret, 0, sizeof(struct cert_info)); if ((ret->pkey = X509_get_pubkey(cert)) == NULL) { msyslog(LOG_ERR, "cert_parse %s\n", ERR_error_string(ERR_get_error(), NULL)); cert_free(ret); X509_free(cert); return (NULL); } ret->version = X509_get_version(cert); X509_NAME_oneline(X509_get_subject_name(cert), pathbuf, MAXFILENAME - 1); ptr = strstr(pathbuf, "CN="); if (ptr == NULL) { msyslog(LOG_INFO, "cert_parse: invalid subject %s", pathbuf); cert_free(ret); X509_free(cert); return (NULL); } ret->subject = emalloc(strlen(ptr) + 1); strcpy(ret->subject, ptr + 3); /* * Extract remaining objects. Note that the NTP serial number is * the NTP seconds at the time of signing, but this might not be * the case for other authority. We don't bother to check the * objects at this time, since the real crunch can happen only * when the time is valid but not yet certificated. */ ret->nid = OBJ_obj2nid(cert->cert_info->signature->algorithm); ret->digest = (const EVP_MD *)EVP_get_digestbynid(ret->nid); ret->serial = (u_long)ASN1_INTEGER_get(X509_get_serialNumber(cert)); X509_NAME_oneline(X509_get_issuer_name(cert), pathbuf, MAXFILENAME); if ((ptr = strstr(pathbuf, "CN=")) == NULL) { msyslog(LOG_INFO, "cert_parse: invalid issuer %s", pathbuf); cert_free(ret); X509_free(cert); return (NULL); } ret->issuer = emalloc(strlen(ptr) + 1); strcpy(ret->issuer, ptr + 3); ret->first = asn2ntp(X509_get_notBefore(cert)); ret->last = asn2ntp(X509_get_notAfter(cert)); /* * Extract extension fields. These are ad hoc ripoffs of * currently assigned functions and will certainly be changed * before prime time. */ cnt = X509_get_ext_count(cert); for (i = 0; i < cnt; i++) { ext = X509_get_ext(cert, i); method = X509V3_EXT_get(ext); temp = OBJ_obj2nid(ext->object); switch (temp) { /* * If a key_usage field is present, we decode whether * this is a trusted or private certificate. This is * dorky; all we want is to compare NIDs, but OpenSSL * insists on BIO text strings. */ case NID_ext_key_usage: bp = BIO_new(BIO_s_mem()); X509V3_EXT_print(bp, ext, 0, 0); BIO_gets(bp, pathbuf, MAXFILENAME); BIO_free(bp); #if DEBUG if (debug) printf("cert_parse: %s: %s\n", OBJ_nid2ln(temp), pathbuf); #endif if (strcmp(pathbuf, "Trust Root") == 0) ret->flags |= CERT_TRUST; else if (strcmp(pathbuf, "Private") == 0) ret->flags |= CERT_PRIV; break; /* * If a NID_subject_key_identifier field is present, it * contains the GQ public key. */ case NID_subject_key_identifier: ret->grplen = ext->value->length - 2; ret->grpkey = emalloc(ret->grplen); memcpy(ret->grpkey, &ext->value->data[2], ret->grplen); break; } } /* * If certificate is self signed, verify signature. */ if (strcmp(ret->subject, ret->issuer) == 0) { if (!X509_verify(cert, ret->pkey)) { msyslog(LOG_INFO, "cert_parse: signature not verified %s", pathbuf); cert_free(ret); X509_free(cert); return (NULL); } } /* * Verify certificate valid times. Note that certificates cannot * be retroactive. */ if (ret->first > ret->last || ret->first < fstamp) { msyslog(LOG_INFO, "cert_parse: invalid certificate %s first %u last %u fstamp %u", ret->subject, ret->first, ret->last, fstamp); cert_free(ret); X509_free(cert); return (NULL); } /* * Build the value structure to sign and send later. */ ret->cert.fstamp = htonl(fstamp); ret->cert.vallen = htonl(len); ret->cert.ptr = emalloc(len); memcpy(ret->cert.ptr, asn1cert, len); #ifdef DEBUG if (debug > 1) X509_print_fp(stdout, cert); #endif X509_free(cert); return (ret); } /* * cert_sign - sign x509 certificate equest and update value structure. * * The certificate request includes a copy of the host certificate, * which includes the version number, subject name and public key of the * host. The resulting certificate includes these values plus the * serial number, issuer name and valid interval of the server. The * valid interval extends from the current time to the same time one * year hence. This may extend the life of the signed certificate beyond * that of the signer certificate. * * It is convenient to use the NTP seconds of the current time as the * serial number. In the value structure the timestamp is the current * time and the filestamp is taken from the extension field. Note this * routine is called only when the client clock is synchronized to a * proventic source, so timestamp comparisons are valid. * * The host certificate is valid from the time it was generated for a * period of one year. A signed certificate is valid from the time of * signature for a period of one year, but only the host certificate (or * sign certificate if used) is actually used to encrypt and decrypt * signatures. The signature trail is built from the client via the * intermediate servers to the trusted server. Each signature on the * trail must be valid at the time of signature, but it could happen * that a signer certificate expire before the signed certificate, which * remains valid until its expiration. * * Returns * XEVNT_OK success * XEVNT_PUB bad or missing public key * XEVNT_CRT bad or missing certificate * XEVNT_VFY certificate not verified * XEVNT_PER host certificate expired */ static int cert_sign( struct exten *ep, /* extension field pointer */ struct value *vp /* value pointer */ ) { X509 *req; /* X509 certificate request */ X509 *cert; /* X509 certificate */ X509_EXTENSION *ext; /* certificate extension */ ASN1_INTEGER *serial; /* serial number */ X509_NAME *subj; /* distinguished (common) name */ EVP_PKEY *pkey; /* public key */ EVP_MD_CTX ctx; /* message digest context */ tstamp_t tstamp; /* NTP timestamp */ u_int len; u_char *ptr; int i, temp; /* * Decode ASN.1 objects and construct certificate structure. * Make sure the system clock is synchronized to a proventic * source. */ tstamp = crypto_time(); if (tstamp == 0) return (XEVNT_TSP); if (tstamp < cinfo->first || tstamp > cinfo->last) return (XEVNT_PER); ptr = (u_char *)ep->pkt; if ((req = d2i_X509(NULL, &ptr, ntohl(ep->vallen))) == NULL) { msyslog(LOG_ERR, "cert_sign %s\n", ERR_error_string(ERR_get_error(), NULL)); return (XEVNT_CRT); } /* * Extract public key and check for errors. */ if ((pkey = X509_get_pubkey(req)) == NULL) { msyslog(LOG_ERR, "cert_sign %s\n", ERR_error_string(ERR_get_error(), NULL)); X509_free(req); return (XEVNT_PUB); } /* * Generate X509 certificate signed by this server. For this * purpose the issuer name is the server name. Also copy any * extensions that might be present. */ cert = X509_new(); X509_set_version(cert, X509_get_version(req)); serial = ASN1_INTEGER_new(); ASN1_INTEGER_set(serial, tstamp); X509_set_serialNumber(cert, serial); X509_gmtime_adj(X509_get_notBefore(cert), 0L); X509_gmtime_adj(X509_get_notAfter(cert), YEAR); subj = X509_get_issuer_name(cert); X509_NAME_add_entry_by_txt(subj, "commonName", MBSTRING_ASC, (u_char *)sys_hostname, strlen(sys_hostname), -1, 0); subj = X509_get_subject_name(req); X509_set_subject_name(cert, subj); X509_set_pubkey(cert, pkey); ext = X509_get_ext(req, 0); temp = X509_get_ext_count(req); for (i = 0; i < temp; i++) { ext = X509_get_ext(req, i); X509_add_ext(cert, ext, -1); } X509_free(req); /* * Sign and verify the certificate. */ X509_sign(cert, sign_pkey, sign_digest); if (!X509_verify(cert, sign_pkey)) { printf("cert_sign\n%s\n", ERR_error_string(ERR_get_error(), NULL)); X509_free(cert); return (XEVNT_VFY); } len = i2d_X509(cert, NULL); /* * Build and sign the value structure. We have to sign it here, * since the response has to be returned right away. This is a * clogging hazard. */ memset(vp, 0, sizeof(struct value)); vp->tstamp = htonl(tstamp); vp->fstamp = ep->fstamp; vp->vallen = htonl(len); vp->ptr = emalloc(len); ptr = vp->ptr; i2d_X509(cert, &ptr); vp->siglen = 0; vp->sig = emalloc(sign_siglen); EVP_SignInit(&ctx, sign_digest); EVP_SignUpdate(&ctx, (u_char *)vp, 12); EVP_SignUpdate(&ctx, vp->ptr, len); if (EVP_SignFinal(&ctx, vp->sig, &len, sign_pkey)) vp->siglen = htonl(len); #ifdef DEBUG if (debug > 1) X509_print_fp(stdout, cert); #endif X509_free(cert); return (XEVNT_OK); } /* * cert_valid - verify certificate with given public key * * This is pretty ugly, as the certificate has to be verified in the * OpenSSL X509 structure, not in the DER format in the info/value * structure. * * Returns * XEVNT_OK success * XEVNT_VFY certificate not verified */ int cert_valid( struct cert_info *cinf, /* certificate information structure */ EVP_PKEY *pkey /* public key */ ) { X509 *cert; /* X509 certificate */ u_char *ptr; if (cinf->flags & CERT_SIGN) return (XEVNT_OK); ptr = (u_char *)cinf->cert.ptr; cert = d2i_X509(NULL, &ptr, ntohl(cinf->cert.vallen)); if (cert == NULL || !X509_verify(cert, pkey)) return (XEVNT_VFY); X509_free(cert); return (XEVNT_OK); } /* * cert - install certificate in certificate list * * This routine encodes an extension field into a certificate info/value * structure. It searches the certificate list for duplicates and * expunges whichever is older. It then searches the list for other * certificates that might be verified by this latest one. Finally, it * inserts this certificate first on the list. * * Returns * XEVNT_OK success * XEVNT_FSP bad or missing filestamp * XEVNT_CRT bad or missing certificate */ int cert_install( struct exten *ep, /* cert info/value */ struct peer *peer /* peer structure */ ) { struct cert_info *cp, *xp, *yp, **zp; /* * Parse and validate the signed certificate. If valid, * construct the info/value structure; otherwise, scamper home. */ if ((cp = cert_parse((u_char *)ep->pkt, ntohl(ep->vallen), ntohl(ep->fstamp))) == NULL) return (XEVNT_CRT); /* * Scan certificate list looking for another certificate with * the same subject and issuer. If another is found with the * same or older filestamp, unlink it and return the goodies to * the heap. If another is found with a later filestamp, discard * the new one and leave the building. * * Make a note to study this issue again. An earlier certificate * with a long lifetime might be overtaken by a later * certificate with a short lifetime, thus invalidating the * earlier signature. However, we gotta find a way to leak old * stuff from the cache, so we do it anyway. */ yp = cp; zp = &cinfo; for (xp = cinfo; xp != NULL; xp = xp->link) { if (strcmp(cp->subject, xp->subject) == 0 && strcmp(cp->issuer, xp->issuer) == 0) { if (ntohl(cp->cert.fstamp) <= ntohl(xp->cert.fstamp)) { *zp = xp->link;; cert_free(xp); } else { cert_free(cp); return (XEVNT_FSP); } break; } zp = &xp->link; } yp->link = cinfo; cinfo = yp; /* * Scan the certificate list to see if Y is signed by X. This is * independent of order. */ for (yp = cinfo; yp != NULL; yp = yp->link) { for (xp = cinfo; xp != NULL; xp = xp->link) { /* * If the issuer of certificate Y matches the * subject of certificate X, verify the * signature of Y using the public key of X. If * so, X signs Y. */ if (strcmp(yp->issuer, xp->subject) != 0 || xp->flags & CERT_ERROR) continue; if (cert_valid(yp, xp->pkey) != XEVNT_OK) { yp->flags |= CERT_ERROR; continue; } /* * The signature Y is valid only if it begins * during the lifetime of X; however, it is not * necessarily an error, since some other * certificate might sign Y. */ if (yp->first < xp->first || yp->first > xp->last) continue; yp->flags |= CERT_SIGN; /* * If X is trusted, then Y is trusted. Note that * we might stumble over a self-signed * certificate that is not trusted, at least * temporarily. This can happen when a dude * first comes up, but has not synchronized the * clock and had its certificate signed by its * server. In case of broken certificate trail, * this might result in a loop that could * persist until timeout. */ if (!(xp->flags & (CERT_TRUST | CERT_VALID))) continue; yp->flags |= CERT_VALID; /* * If subject Y matches the server subject name, * then Y has completed the certificate trail. * Save the group key and light the valid bit. */ if (strcmp(yp->subject, peer->subject) != 0) continue; if (yp->grpkey != NULL) { if (peer->grpkey != NULL) BN_free(peer->grpkey); peer->grpkey = BN_bin2bn(yp->grpkey, yp->grplen, NULL); } peer->crypto |= CRYPTO_FLAG_VALID; /* * If the server has an an identity scheme, * fetch the identity credentials. If not, the * identity is verified only by the trusted * certificate. The next signature will set the * server proventic. */ if (peer->crypto & (CRYPTO_FLAG_GQ | CRYPTO_FLAG_IFF | CRYPTO_FLAG_MV)) continue; peer->crypto |= CRYPTO_FLAG_VRFY; } } /* * That was awesome. Now update the timestamps and signatures. */ crypto_update(); return (XEVNT_OK); } /* * cert_free - free certificate information structure */ void cert_free( struct cert_info *cinf /* certificate info/value structure */ ) { if (cinf->pkey != NULL) EVP_PKEY_free(cinf->pkey); if (cinf->subject != NULL) free(cinf->subject); if (cinf->issuer != NULL) free(cinf->issuer); if (cinf->grpkey != NULL) free(cinf->grpkey); value_free(&cinf->cert); free(cinf); } /* *********************************************************************** * * * The following routines are used only at initialization time * * * *********************************************************************** */ /* * crypto_key - load cryptographic parameters and keys from files * * This routine loads a PEM-encoded public/private key pair and extracts * the filestamp from the file name. * * Returns public key pointer if valid, NULL if not. Side effect updates * the filestamp if valid. */ static EVP_PKEY * crypto_key( char *cp, /* file name */ tstamp_t *fstamp /* filestamp */ ) { FILE *str; /* file handle */ EVP_PKEY *pkey = NULL; /* public/private key */ char filename[MAXFILENAME]; /* name of key file */ char linkname[MAXFILENAME]; /* filestamp buffer) */ char statstr[NTP_MAXSTRLEN]; /* statistics for filegen */ char *ptr; /* * Open the key file. If the first character of the file name is * not '/', prepend the keys directory string. If something goes * wrong, abandon ship. */ if (*cp == '/') strcpy(filename, cp); else snprintf(filename, MAXFILENAME, "%s/%s", keysdir, cp); str = fopen(filename, "r"); if (str == NULL) return (NULL); /* * Read the filestamp, which is contained in the first line. */ if ((ptr = fgets(linkname, MAXFILENAME, str)) == NULL) { msyslog(LOG_ERR, "crypto_key: no data %s\n", filename); (void)fclose(str); return (NULL); } if ((ptr = strrchr(ptr, '.')) == NULL) { msyslog(LOG_ERR, "crypto_key: no filestamp %s\n", filename); (void)fclose(str); return (NULL); } if (sscanf(++ptr, "%u", fstamp) != 1) { msyslog(LOG_ERR, "crypto_key: invalid timestamp %s\n", filename); (void)fclose(str); return (NULL); } /* * Read and decrypt PEM-encoded private key. */ pkey = PEM_read_PrivateKey(str, NULL, NULL, passwd); fclose(str); if (pkey == NULL) { msyslog(LOG_ERR, "crypto_key %s\n", ERR_error_string(ERR_get_error(), NULL)); return (NULL); } /* * Leave tracks in the cryptostats. */ if ((ptr = strrchr(linkname, '\n')) != NULL) *ptr = '\0'; snprintf(statstr, NTP_MAXSTRLEN, "%s mod %d", &linkname[2], EVP_PKEY_size(pkey) * 8); record_crypto_stats(NULL, statstr); #ifdef DEBUG if (debug) printf("crypto_key: %s\n", statstr); if (debug > 1) { if (pkey->type == EVP_PKEY_DSA) DSA_print_fp(stdout, pkey->pkey.dsa, 0); else RSA_print_fp(stdout, pkey->pkey.rsa, 0); } #endif return (pkey); } /* * crypto_cert - load certificate from file * * This routine loads a X.509 RSA or DSA certificate from a file and * constructs a info/cert value structure for this machine. The * structure includes a filestamp extracted from the file name. Later * the certificate can be sent to another machine by request. * * Returns certificate info/value pointer if valid, NULL if not. */ static struct cert_info * /* certificate information */ crypto_cert( char *cp /* file name */ ) { struct cert_info *ret; /* certificate information */ FILE *str; /* file handle */ char filename[MAXFILENAME]; /* name of certificate file */ char linkname[MAXFILENAME]; /* filestamp buffer */ char statstr[NTP_MAXSTRLEN]; /* statistics for filegen */ tstamp_t fstamp; /* filestamp */ long len; char *ptr; char *name, *header; u_char *data; /* * Open the certificate file. If the first character of the file * name is not '/', prepend the keys directory string. If * something goes wrong, abandon ship. */ if (*cp == '/') strcpy(filename, cp); else snprintf(filename, MAXFILENAME, "%s/%s", keysdir, cp); str = fopen(filename, "r"); if (str == NULL) return (NULL); /* * Read the filestamp, which is contained in the first line. */ if ((ptr = fgets(linkname, MAXFILENAME, str)) == NULL) { msyslog(LOG_ERR, "crypto_cert: no data %s\n", filename); (void)fclose(str); return (NULL); } if ((ptr = strrchr(ptr, '.')) == NULL) { msyslog(LOG_ERR, "crypto_cert: no filestamp %s\n", filename); (void)fclose(str); return (NULL); } if (sscanf(++ptr, "%u", &fstamp) != 1) { msyslog(LOG_ERR, "crypto_cert: invalid filestamp %s\n", filename); (void)fclose(str); return (NULL); } /* * Read PEM-encoded certificate and install. */ if (!PEM_read(str, &name, &header, &data, &len)) { msyslog(LOG_ERR, "crypto_cert %s\n", ERR_error_string(ERR_get_error(), NULL)); (void)fclose(str); return (NULL); } free(header); if (strcmp(name, "CERTIFICATE") !=0) { msyslog(LOG_INFO, "crypto_cert: wrong PEM type %s", name); free(name); free(data); (void)fclose(str); return (NULL); } free(name); /* * Parse certificate and generate info/value structure. */ ret = cert_parse(data, len, fstamp); free(data); (void)fclose(str); if (ret == NULL) return (NULL); if ((ptr = strrchr(linkname, '\n')) != NULL) *ptr = '\0'; snprintf(statstr, NTP_MAXSTRLEN, "%s 0x%x len %lu", &linkname[2], ret->flags, len); record_crypto_stats(NULL, statstr); #ifdef DEBUG if (debug) printf("crypto_cert: %s\n", statstr); #endif return (ret); } /* * crypto_tai - load leapseconds table from file * * This routine loads the ERTS leapsecond file in NIST text format, * converts to a value structure and extracts a filestamp from the file * name. The data are used to establish the TAI offset from UTC, which * is provided to the kernel if supported. Later the data can be sent to * another machine on request. */ static void crypto_tai( char *cp /* file name */ ) { FILE *str; /* file handle */ char buf[NTP_MAXSTRLEN]; /* file line buffer */ u_int32 leapsec[MAX_LEAP]; /* NTP time at leaps */ int offset; /* offset at leap (s) */ char filename[MAXFILENAME]; /* name of leapseconds file */ char linkname[MAXFILENAME]; /* file link (for filestamp) */ char statstr[NTP_MAXSTRLEN]; /* statistics for filegen */ tstamp_t fstamp; /* filestamp */ u_int len; u_int32 *ptr; char *dp; int rval, i, j; /* * Open the file and discard comment lines. If the first * character of the file name is not '/', prepend the keys * directory string. If the file is not found, not to worry; it * can be retrieved over the net. But, if it is found with * errors, we crash and burn. */ if (*cp == '/') strcpy(filename, cp); else snprintf(filename, MAXFILENAME, "%s/%s", keysdir, cp); if ((str = fopen(filename, "r")) == NULL) return; /* * Extract filestamp if present. */ rval = readlink(filename, linkname, MAXFILENAME - 1); if (rval > 0) { linkname[rval] = '\0'; dp = strrchr(linkname, '.'); } else { dp = strrchr(filename, '.'); } if (dp != NULL) sscanf(++dp, "%u", &fstamp); else fstamp = 0; tai_leap.fstamp = htonl(fstamp); /* * We are rather paranoid here, since an intruder might cause a * coredump by infiltrating naughty values. Empty lines and * comments are ignored. Other lines must begin with two * integers followed by junk or comments. The first integer is * the NTP seconds of leap insertion, the second is the offset * of TAI relative to UTC after that insertion. The second word * must equal the initial insertion of ten seconds on 1 January * 1972 plus one second for each succeeding insertion. */ i = 0; while (i < MAX_LEAP) { dp = fgets(buf, NTP_MAXSTRLEN - 1, str); if (dp == NULL) break; if (strlen(buf) < 1) continue; if (*buf == '#') continue; if (sscanf(buf, "%u %d", &leapsec[i], &offset) != 2) continue; if (i != offset - TAI_1972) break; i++; } fclose(str); if (dp != NULL) { msyslog(LOG_INFO, "crypto_tai: leapseconds file %s error %d", cp, rval); exit (-1); } /* * The extension field table entries consists of the NTP seconds * of leap insertion in network byte order. */ len = i * sizeof(u_int32); tai_leap.vallen = htonl(len); ptr = emalloc(len); tai_leap.ptr = (u_char *)ptr; for (j = 0; j < i; j++) *ptr++ = htonl(leapsec[j]); crypto_flags |= CRYPTO_FLAG_TAI; snprintf(statstr, NTP_MAXSTRLEN, "%s fs %u leap %u len %u", cp, fstamp, leapsec[--j], len); record_crypto_stats(NULL, statstr); #ifdef DEBUG if (debug) printf("crypto_tai: %s\n", statstr); #endif } /* * crypto_setup - load keys, certificate and leapseconds table * * This routine loads the public/private host key and certificate. If * available, it loads the public/private sign key, which defaults to * the host key, and leapseconds table. The host key must be RSA, but * the sign key can be either RSA or DSA. In either case, the public key * on the certificate must agree with the sign key. */ void crypto_setup(void) { EVP_PKEY *pkey; /* private/public key pair */ char filename[MAXFILENAME]; /* file name buffer */ l_fp seed; /* crypto PRNG seed as NTP timestamp */ tstamp_t fstamp; /* filestamp */ tstamp_t sstamp; /* sign filestamp */ u_int len, bytes; u_char *ptr; /* * Initialize structures. */ if (!crypto_flags) return; gethostname(filename, MAXFILENAME); bytes = strlen(filename) + 1; sys_hostname = emalloc(bytes); memcpy(sys_hostname, filename, bytes); if (passwd == NULL) passwd = sys_hostname; memset(&hostval, 0, sizeof(hostval)); memset(&pubkey, 0, sizeof(pubkey)); memset(&tai_leap, 0, sizeof(tai_leap)); /* * Load required random seed file and seed the random number * generator. Be default, it is found in the user home * directory. The root home directory may be / or /root, * depending on the system. Wiggle the contents a bit and write * it back so the sequence does not repeat when we next restart. */ ERR_load_crypto_strings(); if (rand_file == NULL) { if ((RAND_file_name(filename, MAXFILENAME)) != NULL) { rand_file = emalloc(strlen(filename) + 1); strcpy(rand_file, filename); } } else if (*rand_file != '/') { snprintf(filename, MAXFILENAME, "%s/%s", keysdir, rand_file); free(rand_file); rand_file = emalloc(strlen(filename) + 1); strcpy(rand_file, filename); } if (rand_file == NULL) { msyslog(LOG_ERR, "crypto_setup: random seed file not specified"); exit (-1); } if ((bytes = RAND_load_file(rand_file, -1)) == 0) { msyslog(LOG_ERR, "crypto_setup: random seed file %s not found\n", rand_file); exit (-1); } - get_systime(&seed); + arc4random_buf(&seed, sizeof(l_fp)); RAND_seed(&seed, sizeof(l_fp)); RAND_write_file(rand_file); OpenSSL_add_all_algorithms(); #ifdef DEBUG if (debug) printf( "crypto_setup: OpenSSL version %lx random seed file %s bytes read %d\n", SSLeay(), rand_file, bytes); #endif /* * Load required host key from file "ntpkey_host_". It * also becomes the default sign key. */ if (host_file == NULL) { snprintf(filename, MAXFILENAME, "ntpkey_host_%s", sys_hostname); host_file = emalloc(strlen(filename) + 1); strcpy(host_file, filename); } pkey = crypto_key(host_file, &fstamp); if (pkey == NULL) { msyslog(LOG_ERR, "crypto_setup: host key file %s not found or corrupt", host_file); exit (-1); } host_pkey = pkey; sign_pkey = pkey; sstamp = fstamp; hostval.fstamp = htonl(fstamp); if (host_pkey->type != EVP_PKEY_RSA) { msyslog(LOG_ERR, "crypto_setup: host key is not RSA key type"); exit (-1); } hostval.vallen = htonl(strlen(sys_hostname)); hostval.ptr = (u_char *)sys_hostname; /* * Construct public key extension field for agreement scheme. */ len = i2d_PublicKey(host_pkey, NULL); ptr = emalloc(len); pubkey.ptr = ptr; i2d_PublicKey(host_pkey, &ptr); pubkey.vallen = htonl(len); pubkey.fstamp = hostval.fstamp; /* * Load optional sign key from file "ntpkey_sign_". If * loaded, it becomes the sign key. */ if (sign_file == NULL) { snprintf(filename, MAXFILENAME, "ntpkey_sign_%s", sys_hostname); sign_file = emalloc(strlen(filename) + 1); strcpy(sign_file, filename); } pkey = crypto_key(sign_file, &fstamp); if (pkey != NULL) { sign_pkey = pkey; sstamp = fstamp; } sign_siglen = EVP_PKEY_size(sign_pkey); /* * Load optional IFF parameters from file * "ntpkey_iff_". */ if (iffpar_file == NULL) { snprintf(filename, MAXFILENAME, "ntpkey_iff_%s", sys_hostname); iffpar_file = emalloc(strlen(filename) + 1); strcpy(iffpar_file, filename); } iffpar_pkey = crypto_key(iffpar_file, &if_fstamp); if (iffpar_pkey != NULL) crypto_flags |= CRYPTO_FLAG_IFF; /* * Load optional GQ parameters from file "ntpkey_gq_". */ if (gqpar_file == NULL) { snprintf(filename, MAXFILENAME, "ntpkey_gq_%s", sys_hostname); gqpar_file = emalloc(strlen(filename) + 1); strcpy(gqpar_file, filename); } gqpar_pkey = crypto_key(gqpar_file, &gq_fstamp); if (gqpar_pkey != NULL) crypto_flags |= CRYPTO_FLAG_GQ; /* * Load optional MV parameters from file "ntpkey_mv_". */ if (mvpar_file == NULL) { snprintf(filename, MAXFILENAME, "ntpkey_mv_%s", sys_hostname); mvpar_file = emalloc(strlen(filename) + 1); strcpy(mvpar_file, filename); } mvpar_pkey = crypto_key(mvpar_file, &mv_fstamp); if (mvpar_pkey != NULL) crypto_flags |= CRYPTO_FLAG_MV; /* * Load required certificate from file "ntpkey_cert_". */ if (cert_file == NULL) { snprintf(filename, MAXFILENAME, "ntpkey_cert_%s", sys_hostname); cert_file = emalloc(strlen(filename) + 1); strcpy(cert_file, filename); } if ((cinfo = crypto_cert(cert_file)) == NULL) { msyslog(LOG_ERR, "certificate file %s not found or corrupt", cert_file); exit (-1); } /* * The subject name must be the same as the host name, unless * the certificate is private, in which case it may have come * from another host. */ if (!(cinfo->flags & CERT_PRIV) && strcmp(cinfo->subject, sys_hostname) != 0) { msyslog(LOG_ERR, "crypto_setup: certificate %s not for this host", cert_file); cert_free(cinfo); exit (-1); } /* * It the certificate is trusted, the subject must be the same * as the issuer, in other words it must be self signed. */ if (cinfo->flags & CERT_TRUST && strcmp(cinfo->subject, cinfo->issuer) != 0) { if (cert_valid(cinfo, sign_pkey) != XEVNT_OK) { msyslog(LOG_ERR, "crypto_setup: certificate %s is trusted, but not self signed.", cert_file); cert_free(cinfo); exit (-1); } } sign_digest = cinfo->digest; if (cinfo->flags & CERT_PRIV) crypto_flags |= CRYPTO_FLAG_PRIV; crypto_flags |= cinfo->nid << 16; /* * Load optional leapseconds table from file "ntpkey_leap". If * the file is missing or defective, the values can later be * retrieved from a server. */ if (leap_file == NULL) leap_file = "ntpkey_leap"; crypto_tai(leap_file); #ifdef DEBUG if (debug) printf( "crypto_setup: flags 0x%x host %s signature %s\n", crypto_flags, sys_hostname, OBJ_nid2ln(cinfo->nid)); #endif } /* * crypto_config - configure data from crypto configuration command. */ void crypto_config( int item, /* configuration item */ char *cp /* file name */ ) { switch (item) { /* * Set random seed file name. */ case CRYPTO_CONF_RAND: rand_file = emalloc(strlen(cp) + 1); strcpy(rand_file, cp); break; /* * Set private key password. */ case CRYPTO_CONF_PW: passwd = emalloc(strlen(cp) + 1); strcpy(passwd, cp); break; /* * Set host file name. */ case CRYPTO_CONF_PRIV: host_file = emalloc(strlen(cp) + 1); strcpy(host_file, cp); break; /* * Set sign key file name. */ case CRYPTO_CONF_SIGN: sign_file = emalloc(strlen(cp) + 1); strcpy(sign_file, cp); break; /* * Set iff parameters file name. */ case CRYPTO_CONF_IFFPAR: iffpar_file = emalloc(strlen(cp) + 1); strcpy(iffpar_file, cp); break; /* * Set gq parameters file name. */ case CRYPTO_CONF_GQPAR: gqpar_file = emalloc(strlen(cp) + 1); strcpy(gqpar_file, cp); break; /* * Set mv parameters file name. */ case CRYPTO_CONF_MVPAR: mvpar_file = emalloc(strlen(cp) + 1); strcpy(mvpar_file, cp); break; /* * Set identity scheme. */ case CRYPTO_CONF_IDENT: if (!strcasecmp(cp, "iff")) ident_scheme |= CRYPTO_FLAG_IFF; else if (!strcasecmp(cp, "gq")) ident_scheme |= CRYPTO_FLAG_GQ; else if (!strcasecmp(cp, "mv")) ident_scheme |= CRYPTO_FLAG_MV; break; /* * Set certificate file name. */ case CRYPTO_CONF_CERT: cert_file = emalloc(strlen(cp) + 1); strcpy(cert_file, cp); break; /* * Set leapseconds file name. */ case CRYPTO_CONF_LEAP: leap_file = emalloc(strlen(cp) + 1); strcpy(leap_file, cp); break; } crypto_flags |= CRYPTO_FLAG_ENAB; } # else int ntp_crypto_bs_pubkey; # endif /* OPENSSL */ Index: head/contrib/ntp/ntpd/ntp_proto.c =================================================================== --- head/contrib/ntp/ntpd/ntp_proto.c (revision 276070) +++ head/contrib/ntp/ntpd/ntp_proto.c (revision 276071) @@ -1,3450 +1,3451 @@ /* * ntp_proto.c - NTP version 4 protocol machinery * * ATTENTION: Get approval from Dave Mills on all changes to this file! * */ #ifdef HAVE_CONFIG_H #include #endif #include "ntpd.h" #include "ntp_stdlib.h" #include "ntp_unixtime.h" #include "ntp_control.h" #include "ntp_string.h" #include #if defined(VMS) && defined(VMS_LOCALUNIT) /*wjm*/ #include "ntp_refclock.h" #endif #if defined(__FreeBSD__) && __FreeBSD__ >= 3 #include #endif /* * This macro defines the authentication state. If x is 1 authentication * is required; othewise it is optional. */ #define AUTH(x, y) ((x) ? (y) == AUTH_OK : (y) == AUTH_OK || \ (y) == AUTH_NONE) /* * System variables are declared here. See Section 3.2 of the * specification. */ u_char sys_leap; /* system leap indicator */ u_char sys_stratum; /* stratum of system */ s_char sys_precision; /* local clock precision (log2 s) */ double sys_rootdelay; /* roundtrip delay to primary source */ double sys_rootdispersion; /* dispersion to primary source */ u_int32 sys_refid; /* source/loop in network byte order */ static double sys_offset; /* current local clock offset */ l_fp sys_reftime; /* time we were last updated */ struct peer *sys_peer; /* our current peer */ struct peer *sys_pps; /* our PPS peer */ struct peer *sys_prefer; /* our cherished peer */ int sys_kod; /* kod credit */ int sys_kod_rate = 2; /* max kod packets per second */ #ifdef OPENSSL u_long sys_automax; /* maximum session key lifetime */ #endif /* OPENSSL */ /* * Nonspecified system state variables. */ int sys_bclient; /* broadcast client enable */ double sys_bdelay; /* broadcast client default delay */ int sys_calldelay; /* modem callup delay (s) */ int sys_authenticate; /* requre authentication for config */ l_fp sys_authdelay; /* authentication delay */ static u_long sys_authdly[2]; /* authentication delay shift reg */ static double sys_mindisp = MINDISPERSE; /* min disp increment (s) */ static double sys_maxdist = MAXDISTANCE; /* selection threshold (s) */ double sys_jitter; /* system jitter (s) */ static int sys_hopper; /* anticlockhop counter */ static int sys_maxhop = MAXHOP; /* anticlockhop counter threshold */ int leap_next; /* leap consensus */ keyid_t sys_private; /* private value for session seed */ int sys_manycastserver; /* respond to manycast client pkts */ int peer_ntpdate; /* active peers in ntpdate mode */ int sys_survivors; /* truest of the truechimers */ #ifdef OPENSSL char *sys_hostname; /* gethostname() name */ #endif /* OPENSSL */ /* * TOS and multicast mapping stuff */ int sys_floor = 0; /* cluster stratum floor */ int sys_ceiling = STRATUM_UNSPEC; /* cluster stratum ceiling */ int sys_minsane = 1; /* minimum candidates */ int sys_minclock = NTP_MINCLOCK; /* minimum survivors */ int sys_maxclock = NTP_MAXCLOCK; /* maximum candidates */ int sys_cohort = 0; /* cohort switch */ int sys_orphan = STRATUM_UNSPEC + 1; /* orphan stratum */ double sys_orphandelay = 0; /* orphan root delay */ int sys_beacon = BEACON; /* manycast beacon interval */ int sys_ttlmax; /* max ttl mapping vector index */ u_char sys_ttl[MAX_TTL]; /* ttl mapping vector */ /* * Statistics counters */ u_long sys_stattime; /* time since reset */ u_long sys_received; /* packets received */ u_long sys_processed; /* packets processed */ u_long sys_newversionpkt; /* current version */ u_long sys_oldversionpkt; /* recent version */ u_long sys_unknownversion; /* invalid version */ u_long sys_restricted; /* access denied */ u_long sys_badlength; /* bad length or format */ u_long sys_badauth; /* bad authentication */ u_long sys_limitrejected; /* rate exceeded */ static double root_distance P((struct peer *)); static void clock_combine P((struct peer **, int)); static void peer_xmit P((struct peer *)); static void fast_xmit P((struct recvbuf *, int, keyid_t, int)); static void clock_update P((void)); static int default_get_precision P((void)); static int peer_unfit P((struct peer *)); /* * transmit - Transmit Procedure. See Section 3.4.2 of the * specification. */ void transmit( struct peer *peer /* peer structure pointer */ ) { int hpoll; /* * The polling state machine. There are two kinds of machines, * those that never expect a reply (broadcast and manycast * server modes) and those that do (all other modes). The dance * is intricate... */ /* * Orphan mode is active when enabled and when no servers less * than the orphan statum are available. In this mode packets * are sent at the orphan stratum. An orphan with no other * synchronization source is an orphan parent. It assumes root * delay zero and reference ID the loopback address. All others * are orphan children with root delay randomized over a 1-s * range. The root delay is used by the election algorithm to * select the order of synchronization. */ hpoll = peer->hpoll; if (sys_orphan < STRATUM_UNSPEC && sys_peer == NULL) { sys_leap = LEAP_NOWARNING; sys_stratum = sys_orphan; sys_refid = htonl(LOOPBACKADR); sys_rootdelay = 0; sys_rootdispersion = 0; } /* * In broadcast mode the poll interval is never changed from * minpoll. */ if (peer->cast_flags & (MDF_BCAST | MDF_MCAST)) { peer->outdate = current_time; peer_xmit(peer); poll_update(peer, hpoll); return; } /* * In manycast mode we start with unity ttl. The ttl is * increased by one for each poll until either sys_maxclock * servers have been found or the maximum ttl is reached. When * sys_maxclock servers are found we stop polling until one or * more servers have timed out or until less than minpoll * associations turn up. In this case additional better servers * are dragged in and preempt the existing ones. */ if (peer->cast_flags & MDF_ACAST) { peer->outdate = current_time; if (peer->unreach > sys_beacon) { peer->unreach = 0; peer->ttl = 0; peer_xmit(peer); } else if (sys_survivors < sys_minclock || peer_preempt < sys_maxclock) { if (peer->ttl < sys_ttlmax) peer->ttl++; peer_xmit(peer); } peer->unreach++; poll_update(peer, hpoll); return; } /* * In unicast modes the dance is much more intricate. It is * desigmed to back off whenever possible to minimize network * traffic. */ if (peer->burst == 0) { u_char oreach; /* * Update the reachability status. If not heard for * three consecutive polls, stuff infinity in the clock * filter. */ oreach = peer->reach; peer->outdate = current_time; if (peer == sys_peer) sys_hopper++; peer->reach <<= 1; if (!(peer->reach & 0x07)) clock_filter(peer, 0., 0., MAXDISPERSE); if (!peer->reach) { /* * Here the peer is unreachable. If it was * previously reachable, raise a trap. */ if (oreach) { report_event(EVNT_UNREACH, peer); peer->timereachable = current_time; } /* * Send a burst if enabled, but only once after * a peer becomes unreachable. If the prempt * flag is dim, bump the unreach counter by one; * otherwise, bump it by three. */ if (peer->flags & FLAG_IBURST && peer->unreach == 0) { peer->burst = NTP_BURST; } if (!(peer->flags & FLAG_PREEMPT)) peer->unreach++; else peer->unreach += 3; } else { /* * Here the peer is reachable. Set the poll * interval to the system poll interval. Send a * burst only if enabled and the peer is fit. * * Respond to the peer evaluation produced by * the selection algorithm. If less than the * outlyer level, up the unreach by three. If * there are excess associations, up the unreach * by two if not a candidate and by one if so. */ if (!(peer->flags & FLAG_PREEMPT)) { peer->unreach = 0; } else if (peer->status < CTL_PST_SEL_SELCAND) { peer->unreach += 3; } else if (peer_preempt > sys_maxclock) { if (peer->status < CTL_PST_SEL_SYNCCAND) peer->unreach += 2; else peer->unreach++; } else { peer->unreach = 0; } hpoll = sys_poll; if (peer->flags & FLAG_BURST && !peer_unfit(peer)) peer->burst = NTP_BURST; } /* * Watch for timeout. If ephemeral or preemptable, toss * the rascal; otherwise, bump the poll interval. */ if (peer->unreach >= NTP_UNREACH) { if (peer->flags & FLAG_PREEMPT || !(peer->flags & FLAG_CONFIG)) { peer_clear(peer, "TIME"); unpeer(peer); return; } else { hpoll++; } } } else { peer->burst--; /* * If a broadcast client at this point, the burst has * concluded, so we switch to client mode and purge the * keylist, since no further transmissions will be made. */ if (peer->burst == 0) { if (peer->cast_flags & MDF_BCLNT) { peer->hmode = MODE_BCLIENT; #ifdef OPENSSL key_expire(peer); #endif /* OPENSSL */ } /* * If ntpdate mode and the clock has not been * set and all peers have completed the burst, * we declare a successful failure. */ if (mode_ntpdate) { peer_ntpdate--; if (peer_ntpdate == 0) { msyslog(LOG_NOTICE, "no reply; clock not set"); exit (0); } } } } /* * Do not transmit if in broadcast client mode. */ if (peer->hmode != MODE_BCLIENT) peer_xmit(peer); poll_update(peer, hpoll); } /* * receive - Receive Procedure. See section 3.4.3 in the specification. */ void receive( struct recvbuf *rbufp ) { register struct peer *peer; /* peer structure pointer */ register struct pkt *pkt; /* receive packet pointer */ int hisversion; /* packet version */ int hisleap; /* packet leap indicator */ int hismode; /* packet mode */ int hisstratum; /* packet stratum */ int restrict_mask; /* restrict bits */ int has_mac; /* length of MAC field */ int authlen; /* offset of MAC field */ int is_authentic = 0; /* cryptosum ok */ keyid_t skeyid = 0; /* key ID */ struct sockaddr_storage *dstadr_sin; /* active runway */ struct peer *peer2; /* aux peer structure pointer */ l_fp p_org; /* origin timestamp */ l_fp p_rec; /* receive timestamp */ l_fp p_xmt; /* transmit timestamp */ #ifdef OPENSSL keyid_t tkeyid = 0; /* temporary key ID */ keyid_t pkeyid = 0; /* previous key ID */ struct autokey *ap; /* autokey structure pointer */ int rval; /* cookie snatcher */ #endif /* OPENSSL */ int retcode = AM_NOMATCH; int at_listhead; /* * Monitor the packet and get restrictions. Note that the packet * length for control and private mode packets must be checked * by the service routines. Note that no statistics counters are * recorded for restrict violations, since these counters are in * the restriction routine. Note the careful distinctions here * between a packet with a format error and a packet that is * simply discarded without prejudice. Some restrictions have to * be handled later in order to generate a kiss-of-death packet. */ /* * Bogus port check is before anything, since it probably * reveals a clogging attack. */ sys_received++; if (SRCPORT(&rbufp->recv_srcadr) == 0) { sys_badlength++; return; /* bogus port */ } at_listhead = ntp_monitor(rbufp); restrict_mask = restrictions(&rbufp->recv_srcadr, at_listhead); #ifdef DEBUG if (debug > 1) printf("receive: at %ld %s<-%s flags %x restrict %03x\n", current_time, stoa(&rbufp->dstadr->sin), stoa(&rbufp->recv_srcadr), rbufp->dstadr->flags, restrict_mask); #endif if (restrict_mask & RES_IGNORE) { sys_restricted++; return; /* ignore everything */ } pkt = &rbufp->recv_pkt; hisversion = PKT_VERSION(pkt->li_vn_mode); hisleap = PKT_LEAP(pkt->li_vn_mode); hismode = (int)PKT_MODE(pkt->li_vn_mode); hisstratum = PKT_TO_STRATUM(pkt->stratum); if (hismode == MODE_PRIVATE) { if (restrict_mask & RES_NOQUERY) { sys_restricted++; return; /* no query private */ } process_private(rbufp, ((restrict_mask & RES_NOMODIFY) == 0)); return; } if (hismode == MODE_CONTROL) { if (restrict_mask & RES_NOQUERY) { sys_restricted++; return; /* no query control */ } process_control(rbufp, restrict_mask); return; } if (restrict_mask & RES_DONTSERVE) { sys_restricted++; return; /* no time */ } if (rbufp->recv_length < LEN_PKT_NOMAC) { sys_badlength++; return; /* runt packet */ } /* * Version check must be after the query packets, since they * intentionally use early version. */ if (hisversion == NTP_VERSION) { sys_newversionpkt++; /* new version */ } else if (!(restrict_mask & RES_VERSION) && hisversion >= NTP_OLDVERSION) { sys_oldversionpkt++; /* previous version */ } else { sys_unknownversion++; return; /* old version */ } /* * Figure out his mode and validate the packet. This has some * legacy raunch that probably should be removed. In very early * NTP versions mode 0 was equivalent to what later versions * would interpret as client mode. */ if (hismode == MODE_UNSPEC) { if (hisversion == NTP_OLDVERSION) { hismode = MODE_CLIENT; } else { sys_badlength++; return; /* invalid mode */ } } /* * Parse the extension field if present. We figure out whether * an extension field is present by measuring the MAC size. If * the number of words following the packet header is 0, no MAC * is present and the packet is not authenticated. If 1, the * packet is a crypto-NAK; if 3, the packet is authenticated * with DES; if 5, the packet is authenticated with MD5. If 2 or * 4, the packet is a runt and discarded forthwith. If greater * than 5, an extension field is present, so we subtract the * length of the field and go around again. */ authlen = LEN_PKT_NOMAC; has_mac = rbufp->recv_length - authlen; while (has_mac > 0) { int temp; if (has_mac % 4 != 0 || has_mac < 0) { sys_badlength++; return; /* bad MAC length */ } if (has_mac == 1 * 4 || has_mac == 3 * 4 || has_mac == MAX_MAC_LEN) { skeyid = ntohl(((u_int32 *)pkt)[authlen / 4]); break; } else if (has_mac > MAX_MAC_LEN) { temp = ntohl(((u_int32 *)pkt)[authlen / 4]) & 0xffff; if (temp < 4 || temp > NTP_MAXEXTEN || temp % 4 != 0) { sys_badlength++; return; /* bad MAC length */ } authlen += temp; has_mac -= temp; } else { sys_badlength++; return; /* bad MAC length */ } } #ifdef OPENSSL pkeyid = tkeyid = 0; #endif /* OPENSSL */ /* * We have tossed out as many buggy packets as possible early in * the game to reduce the exposure to a clogging attack. Now we * have to burn some cycles to find the association and * authenticate the packet if required. Note that we burn only * MD5 cycles, again to reduce exposure. There may be no * matching association and that's okay. * * More on the autokey mambo. Normally the local interface is * found when the association was mobilized with respect to a * designated remote address. We assume packets arriving from * the remote address arrive via this interface and the local * address used to construct the autokey is the unicast address * of the interface. However, if the sender is a broadcaster, * the interface broadcast address is used instead. & Notwithstanding this technobabble, if the sender is a * multicaster, the broadcast address is null, so we use the * unicast address anyway. Don't ask. */ peer = findpeer(&rbufp->recv_srcadr, rbufp->dstadr, hismode, &retcode); dstadr_sin = &rbufp->dstadr->sin; NTOHL_FP(&pkt->org, &p_org); NTOHL_FP(&pkt->rec, &p_rec); NTOHL_FP(&pkt->xmt, &p_xmt); /* * Authentication is conditioned by three switches: * * NOPEER (RES_NOPEER) do not mobilize an association unless * authenticated * NOTRUST (RES_DONTTRUST) do not allow access unless * authenticated (implies NOPEER) * enable (sys_authenticate) master NOPEER switch, by default * on * * The NOPEER and NOTRUST can be specified on a per-client basis * using the restrict command. The enable switch if on implies * NOPEER for all clients. There are four outcomes: * * NONE The packet has no MAC. * OK the packet has a MAC and authentication succeeds * ERROR the packet has a MAC and authentication fails * CRYPTO crypto-NAK. The MAC has four octets only. * * Note: The AUTH(x, y) macro is used to filter outcomes. If x * is zero, acceptable outcomes of y are NONE and OK. If x is * one, the only acceptable outcome of y is OK. */ if (has_mac == 0) { is_authentic = AUTH_NONE; /* not required */ #ifdef DEBUG if (debug) printf("receive: at %ld %s<-%s mode %d code %d auth %d\n", current_time, stoa(dstadr_sin), stoa(&rbufp->recv_srcadr), hismode, retcode, is_authentic); #endif } else if (has_mac == 4) { is_authentic = AUTH_CRYPTO; /* crypto-NAK */ #ifdef DEBUG if (debug) printf( "receive: at %ld %s<-%s mode %d code %d keyid %08x len %d mac %d auth %d\n", current_time, stoa(dstadr_sin), stoa(&rbufp->recv_srcadr), hismode, retcode, skeyid, authlen, has_mac, is_authentic); #endif } else { #ifdef OPENSSL /* * For autokey modes, generate the session key * and install in the key cache. Use the socket * broadcast or unicast address as appropriate. */ if (skeyid > NTP_MAXKEY) { /* * More on the autokey dance (AKD). A cookie is * constructed from public and private values. * For broadcast packets, the cookie is public * (zero). For packets that match no * association, the cookie is hashed from the * addresses and private value. For server * packets, the cookie was previously obtained * from the server. For symmetric modes, the * cookie was previously constructed using an * agreement protocol; however, should PKI be * unavailable, we construct a fake agreement as * the EXOR of the peer and host cookies. * * hismode ephemeral persistent * ======================================= * active 0 cookie# * passive 0% cookie# * client sys cookie 0% * server 0% sys cookie * broadcast 0 0 * * # if unsync, 0 * % can't happen */ if (hismode == MODE_BROADCAST) { /* * For broadcaster, use the interface * broadcast address when available; * otherwise, use the unicast address * found when the association was * mobilized. However, if this is from * the wildcard interface, game over. */ if (crypto_flags && rbufp->dstadr == any_interface) { sys_restricted++; return; /* no wildcard */ } pkeyid = 0; if (!SOCKNUL(&rbufp->dstadr->bcast)) dstadr_sin = &rbufp->dstadr->bcast; } else if (peer == NULL) { pkeyid = session_key( &rbufp->recv_srcadr, dstadr_sin, 0, sys_private, 0); } else { pkeyid = peer->pcookie; } /* * The session key includes both the public * values and cookie. In case of an extension * field, the cookie used for authentication * purposes is zero. Note the hash is saved for * use later in the autokey mambo. */ if (authlen > LEN_PKT_NOMAC && pkeyid != 0) { session_key(&rbufp->recv_srcadr, dstadr_sin, skeyid, 0, 2); tkeyid = session_key( &rbufp->recv_srcadr, dstadr_sin, skeyid, pkeyid, 0); } else { tkeyid = session_key( &rbufp->recv_srcadr, dstadr_sin, skeyid, pkeyid, 2); } } #endif /* OPENSSL */ /* * Compute the cryptosum. Note a clogging attack may * succeed in bloating the key cache. If an autokey, * purge it immediately, since we won't be needing it * again. If the packet is authentic, it can mobilize an * association. Note that there is no key zero. */ if (!authdecrypt(skeyid, (u_int32 *)pkt, authlen, has_mac)) { is_authentic = AUTH_ERROR; sys_badauth++; + return; } else { is_authentic = AUTH_OK; } #ifdef OPENSSL if (skeyid > NTP_MAXKEY) authtrust(skeyid, 0); #endif /* OPENSSL */ #ifdef DEBUG if (debug) printf( "receive: at %ld %s<-%s mode %d code %d keyid %08x len %d mac %d auth %d\n", current_time, stoa(dstadr_sin), stoa(&rbufp->recv_srcadr), hismode, retcode, skeyid, authlen, has_mac, is_authentic); #endif } /* * The association matching rules are implemented by a set of * routines and an association table. A packet matching an * association is processed by the peer process for that * association. If there are no errors, an ephemeral association * is mobilized: a broadcast packet mobilizes a broadcast client * aassociation; a manycast server packet mobilizes a manycast * client association; a symmetric active packet mobilizes a * symmetric passive association. */ switch (retcode) { /* * This is a client mode packet not matching any association. If * an ordinary client, simply toss a server mode packet back * over the fence. If a manycast client, we have to work a * little harder. */ case AM_FXMIT: /* * The vanilla case is when this is not a multicast * interface. If authentication succeeds, return a * server mode packet; if not and the key ID is nonzero, * return a crypto-NAK. */ if (!(rbufp->dstadr->flags & INT_MCASTOPEN)) { if (AUTH(restrict_mask & RES_DONTTRUST, is_authentic)) fast_xmit(rbufp, MODE_SERVER, skeyid, restrict_mask); else if (is_authentic == AUTH_ERROR) fast_xmit(rbufp, MODE_SERVER, 0, restrict_mask); return; /* hooray */ } /* * This must be manycast. Do not respond if not * configured as a manycast server. */ if (!sys_manycastserver) { sys_restricted++; return; /* not enabled */ } /* * Do not respond if unsynchronized or stratum is below * the floor or at or above the ceiling. */ if (sys_leap == LEAP_NOTINSYNC || sys_stratum < sys_floor || sys_stratum >= sys_ceiling) return; /* bad stratum */ /* * Do not respond if our stratum is greater than the * manycaster or it has already synchronized to us. */ if (sys_peer == NULL || hisstratum < sys_stratum || (sys_cohort && hisstratum == sys_stratum) || rbufp->dstadr->addr_refid == pkt->refid) return; /* no help */ /* * Respond only if authentication succeeds. Don't do a * crypto-NAK, as that would not be useful. */ if (AUTH(restrict_mask & RES_DONTTRUST, is_authentic)) fast_xmit(rbufp, MODE_SERVER, skeyid, restrict_mask); return; /* hooray */ /* * This is a server mode packet returned in response to a client * mode packet sent to a multicast group address. The origin * timestamp is a good nonce to reliably associate the reply * with what was sent. If there is no match, that's curious and * could be an intruder attempting to clog, so we just ignore * it. * * If the packet is authentic and the manycast association is * found, we mobilize a client association and copy pertinent * variables from the manycast association to the new client * association. If not, just ignore the packet. * * There is an implosion hazard at the manycast client, since * the manycast servers send the server packet immediately. If * the guy is already here, don't fire up a duplicate. */ case AM_MANYCAST: if (!AUTH(sys_authenticate | (restrict_mask & (RES_NOPEER | RES_DONTTRUST)), is_authentic)) return; /* bad auth */ if ((peer2 = findmanycastpeer(rbufp)) == NULL) { sys_restricted++; return; /* not enabled */ } if ((peer = newpeer(&rbufp->recv_srcadr, rbufp->dstadr, MODE_CLIENT, hisversion, NTP_MINDPOLL, NTP_MAXDPOLL, FLAG_IBURST | FLAG_PREEMPT, MDF_UCAST | MDF_ACLNT, 0, skeyid)) == NULL) return; /* system error */ /* * We don't need these, but it warms the billboards. */ peer->ttl = peer2->ttl; break; /* * This is the first packet received from a broadcast server. If * the packet is authentic and we are enabled as broadcast * client, mobilize a broadcast client association. We don't * kiss any frogs here. */ case AM_NEWBCL: if (!AUTH(sys_authenticate | (restrict_mask & (RES_NOPEER | RES_DONTTRUST)), is_authentic)) return; /* bad auth */ /* * Do not respond if unsynchronized or stratum is below * the floor or at or above the ceiling. */ if (hisleap == LEAP_NOTINSYNC || hisstratum < sys_floor || hisstratum >= sys_ceiling) return; /* bad stratum */ switch (sys_bclient) { /* * If not enabled, just skedaddle. */ case 0: sys_restricted++; return; /* not enabled */ /* * Execute the initial volley in order to calibrate the * propagation delay and run the Autokey protocol, if * enabled. */ case 1: if ((peer = newpeer(&rbufp->recv_srcadr, rbufp->dstadr, MODE_CLIENT, hisversion, NTP_MINDPOLL, NTP_MAXDPOLL, FLAG_MCAST | FLAG_IBURST, MDF_BCLNT, 0, skeyid)) == NULL) return; /* system error */ #ifdef OPENSSL if (skeyid > NTP_MAXKEY) crypto_recv(peer, rbufp); #endif /* OPENSSL */ return; /* hooray */ /* * Do not execute the initial volley. */ case 2: #ifdef OPENSSL /* * If a two-way exchange is not possible, * neither is Autokey. */ if (skeyid > NTP_MAXKEY) { msyslog(LOG_INFO, "receive: autokey requires two-way communication"); return; /* no autokey */ } #endif /* OPENSSL */ if ((peer = newpeer(&rbufp->recv_srcadr, rbufp->dstadr, MODE_BCLIENT, hisversion, NTP_MINDPOLL, NTP_MAXDPOLL, 0, MDF_BCLNT, 0, skeyid)) == NULL) return; /* system error */ } break; /* * This is the first packet received from a symmetric active * peer. If the packet is authentic and the first he sent, * mobilize a passive association. If not, kiss the frog. */ case AM_NEWPASS: /* * If the inbound packet is correctly authenticated and * enabled, a symmetric passive association is * mobilized. If not but correctly authenticated, a * symmetric active response is sent. If authentication * fails, send a crypto-NAK packet. */ if (!AUTH(restrict_mask & RES_DONTTRUST, is_authentic)) { if (is_authentic == AUTH_ERROR) fast_xmit(rbufp, MODE_ACTIVE, 0, restrict_mask); return; /* bad auth */ } if (!AUTH(sys_authenticate | (restrict_mask & RES_NOPEER), is_authentic)) { fast_xmit(rbufp, MODE_ACTIVE, skeyid, restrict_mask); return; /* hooray */ } /* * Do not respond if stratum is below the floor. */ if (hisstratum < sys_floor) return; /* bad stratum */ if ((peer = newpeer(&rbufp->recv_srcadr, rbufp->dstadr, MODE_PASSIVE, hisversion, NTP_MINDPOLL, NTP_MAXDPOLL, 0, MDF_UCAST, 0, skeyid)) == NULL) return; /* system error */ break; /* * Process regular packet. Nothing special. */ case AM_PROCPKT: break; /* * A passive packet matches a passive association. This is * usually the result of reconfiguring a client on the fly. As * this association might be legitamate and this packet an * attempt to deny service, just ignore it. */ case AM_ERR: return; /* * For everything else there is the bit bucket. */ default: return; } peer->flash &= ~PKT_TEST_MASK; /* * Next comes a rigorous schedule of timestamp checking. If the * transmit timestamp is zero, the server is horribly broken. */ if (L_ISZERO(&p_xmt)) { return; /* read rfc1305 */ /* * If the transmit timestamp duplicates a previous one, the * packet is a replay. This prevents the bad guys from replaying * the most recent packet, authenticated or not. */ } else if (L_ISEQU(&peer->org, &p_xmt)) { peer->flash |= TEST1; peer->oldpkt++; return; /* duplicate packet */ /* * If this is a broadcast mode packet, skip further checking. */ } else if (hismode != MODE_BROADCAST) { if (L_ISZERO(&p_org)) peer->flash |= TEST3; /* protocol unsynch */ else if (!L_ISEQU(&p_org, &peer->xmt)) peer->flash |= TEST2; /* bogus packet */ } /* * Update the origin and destination timestamps. If * unsynchronized or bogus abandon ship. If the crypto machine * breaks, light the crypto bit and plaint the log. */ peer->org = p_xmt; peer->rec = rbufp->recv_time; if (peer->flash & PKT_TEST_MASK) { #ifdef OPENSSL if (crypto_flags && (peer->flags & FLAG_SKEY)) { rval = crypto_recv(peer, rbufp); if (rval != XEVNT_OK) { peer_clear(peer, "CRYP"); peer->flash |= TEST9; /* crypto error */ } } #endif /* OPENSSL */ return; /* unsynch */ } /* * The timestamps are valid and the receive packet matches the * last one sent. If the packet is a crypto-NAK, the server * might have just changed keys. We reset the association * and restart the protocol. */ if (is_authentic == AUTH_CRYPTO) { peer_clear(peer, "AUTH"); return; /* crypto-NAK */ /* * If the association is authenticated, the key ID is nonzero * and received packets must be authenticated. This is designed * to avoid a bait-and-switch attack, which was possible in past * versions. If symmetric modes, return a crypto-NAK. The peer * should restart the protocol. */ } else if (!AUTH(peer->keyid || (restrict_mask & RES_DONTTRUST), is_authentic)) { peer->flash |= TEST5; if (hismode == MODE_ACTIVE || hismode == MODE_PASSIVE) fast_xmit(rbufp, MODE_ACTIVE, 0, restrict_mask); return; /* bad auth */ } /* * That was hard and I am sweaty, but the packet is squeaky * clean. Get on with real work. */ peer->received++; peer->timereceived = current_time; if (is_authentic == AUTH_OK) peer->flags |= FLAG_AUTHENTIC; else peer->flags &= ~FLAG_AUTHENTIC; #ifdef OPENSSL /* * More autokey dance. The rules of the cha-cha are as follows: * * 1. If there is no key or the key is not auto, do nothing. * * 2. If this packet is in response to the one just previously * sent or from a broadcast server, do the extension fields. * Otherwise, assume bogosity and bail out. * * 3. If an extension field contains a verified signature, it is * self-authenticated and we sit the dance. * * 4. If this is a server reply, check only to see that the * transmitted key ID matches the received key ID. * * 5. Check to see that one or more hashes of the current key ID * matches the previous key ID or ultimate original key ID * obtained from the broadcaster or symmetric peer. If no * match, sit the dance and wait for timeout. * * In case of crypto error, fire the orchestra and stop dancing. * This is considered a permanant error, so light the crypto bit * to suppress further requests. If preemptable or ephemeral, * scuttle the ship. */ if (crypto_flags && (peer->flags & FLAG_SKEY)) { peer->flash |= TEST8; rval = crypto_recv(peer, rbufp); if (rval != XEVNT_OK) { peer_clear(peer, "CRYP"); peer->flash |= TEST9; /* crypto error */ if (peer->flags & FLAG_PREEMPT || !(peer->flags & FLAG_CONFIG)) unpeer(peer); return; } else if (hismode == MODE_SERVER) { if (skeyid == peer->keyid) peer->flash &= ~TEST8; } else if (!(peer->flash & TEST8)) { peer->pkeyid = skeyid; } else if ((ap = (struct autokey *)peer->recval.ptr) != NULL) { int i; for (i = 0; ; i++) { if (tkeyid == peer->pkeyid || tkeyid == ap->key) { peer->flash &= ~TEST8; peer->pkeyid = skeyid; break; } if (i > ap->seq) break; tkeyid = session_key( &rbufp->recv_srcadr, dstadr_sin, tkeyid, pkeyid, 0); } } if (!(peer->crypto & CRYPTO_FLAG_PROV)) /* test 9 */ peer->flash |= TEST8; /* not proventic */ /* * If the transmit queue is nonempty, clamp the host * poll interval to the packet poll interval. */ if (peer->cmmd != 0) { peer->ppoll = pkt->ppoll; poll_update(peer, peer->hpoll); } } #endif /* OPENSSL */ /* * The dance is complete and the flash bits have been lit. Toss * the packet over the fence for processing, which may light up * more flashers. */ process_packet(peer, pkt); /* * Well, that was nice. If TEST4 is lit, either the crypto * machine jammed or a kiss-o'-death packet flew in, either of * which is fatal. */ if (peer->flash & TEST4) { msyslog(LOG_INFO, "receive: fatal error %04x for %s", peer->flash, stoa(&peer->srcadr)); return; } } /* * process_packet - Packet Procedure, a la Section 3.4.4 of the * specification. Or almost, at least. If we're in here we have a * reasonable expectation that we will be having a long term * relationship with this host. */ void process_packet( register struct peer *peer, register struct pkt *pkt ) { double t34, t21; double p_offset, p_del, p_disp; l_fp p_rec, p_xmt, p_org, p_reftime; l_fp ci; u_char pmode, pleap, pstratum; sys_processed++; peer->processed++; p_del = FPTOD(NTOHS_FP(pkt->rootdelay)); p_disp = FPTOD(NTOHS_FP(pkt->rootdispersion)); NTOHL_FP(&pkt->reftime, &p_reftime); NTOHL_FP(&pkt->rec, &p_rec); NTOHL_FP(&pkt->xmt, &p_xmt); pmode = PKT_MODE(pkt->li_vn_mode); pleap = PKT_LEAP(pkt->li_vn_mode); if (pmode != MODE_BROADCAST) NTOHL_FP(&pkt->org, &p_org); else p_org = peer->rec; pstratum = PKT_TO_STRATUM(pkt->stratum); /* * Test for kiss-o'death packet) */ if (pleap == LEAP_NOTINSYNC && pstratum == STRATUM_UNSPEC) { if (memcmp(&pkt->refid, "DENY", 4) == 0) { peer_clear(peer, "DENY"); peer->flash |= TEST4; /* access denied */ } } /* * Capture the header values. */ record_raw_stats(&peer->srcadr, peer->dstadr ? &peer->dstadr->sin : NULL, &p_org, &p_rec, &p_xmt, &peer->rec); peer->leap = pleap; peer->stratum = min(pstratum, STRATUM_UNSPEC); peer->pmode = pmode; peer->ppoll = pkt->ppoll; peer->precision = pkt->precision; peer->rootdelay = p_del; peer->rootdispersion = p_disp; peer->refid = pkt->refid; /* network byte order */ peer->reftime = p_reftime; /* * Verify the server is synchronized; that is, the leap bits and * stratum are valid, the root delay and root dispersion are * valid and the reference timestamp is not later than the * transmit timestamp. */ if (pleap == LEAP_NOTINSYNC || /* test 6 */ pstratum < sys_floor || pstratum >= sys_ceiling) peer->flash |= TEST6; /* peer not synch */ if (p_del < 0 || p_disp < 0 || p_del / /* test 7 */ 2 + p_disp >= MAXDISPERSE || !L_ISHIS(&p_xmt, &p_reftime)) peer->flash |= TEST7; /* bad header */ /* * If any tests fail at this point, the packet is discarded. * Note that some flashers may have already been set in the * receive() routine. */ if (peer->flash & PKT_TEST_MASK) { #ifdef DEBUG if (debug) printf("packet: flash header %04x\n", peer->flash); #endif return; } if (!(peer->reach)) { report_event(EVNT_REACH, peer); peer->timereachable = current_time; } poll_update(peer, peer->hpoll); peer->reach |= 1; /* * For a client/server association, calculate the clock offset, * roundtrip delay and dispersion. The equations are reordered * from the spec for more efficient use of temporaries. For a * broadcast association, offset the last measurement by the * computed delay during the client/server volley. Note that * org has been set to the time of last reception. Note the * computation of dispersion includes the system precision plus * that due to the frequency error since the origin time. * * It is very important to respect the hazards of overflow. The * only permitted operation on raw timestamps is subtraction, * where the result is a signed quantity spanning from 68 years * in the past to 68 years in the future. To avoid loss of * precision, these calculations are done using 64-bit integer * arithmetic. However, the offset and delay calculations are * sums and differences of these first-order differences, which * if done using 64-bit integer arithmetic, would be valid over * only half that span. Since the typical first-order * differences are usually very small, they are converted to 64- * bit doubles and all remaining calculations done in floating- * point arithmetic. This preserves the accuracy while retaining * the 68-year span. * * Let t1 = p_org, t2 = p_rec, t3 = p_xmt, t4 = peer->rec: */ ci = p_xmt; /* t3 - t4 */ L_SUB(&ci, &peer->rec); LFPTOD(&ci, t34); ci = p_rec; /* t2 - t1 */ L_SUB(&ci, &p_org); LFPTOD(&ci, t21); ci = peer->rec; /* t4 - t1 */ L_SUB(&ci, &p_org); /* * If running in a broadcast association, the clock offset is * (t1 - t0) corrected by the one-way delay, but we can't * measure that directly. Therefore, we start up in MODE_CLIENT * mode, set FLAG_MCAST and exchange eight messages to determine * the clock offset. When the last message is sent, we switch to * MODE_BCLIENT mode. The next broadcast message after that * computes the broadcast offset and clears FLAG_MCAST. */ if (pmode == MODE_BROADCAST) { p_offset = t34; if (peer->flags & FLAG_MCAST) { peer->estbdelay = peer->offset - p_offset; if (peer->hmode == MODE_CLIENT) return; peer->flags &= ~(FLAG_MCAST | FLAG_BURST); } p_offset += peer->estbdelay; p_del = peer->delay; p_disp = 0; } else { p_offset = (t21 + t34) / 2.; p_del = t21 - t34; LFPTOD(&ci, p_disp); p_disp = LOGTOD(sys_precision) + LOGTOD(peer->precision) + clock_phi * p_disp; } p_del = max(p_del, LOGTOD(sys_precision)); clock_filter(peer, p_offset, p_del, p_disp); record_peer_stats(&peer->srcadr, ctlpeerstatus(peer), peer->offset, peer->delay, peer->disp, peer->jitter); } /* * clock_update - Called at system process update intervals. */ static void clock_update(void) { u_char oleap; u_char ostratum; double dtemp; /* * There must be a system peer at this point. If we just changed * the system peer, but have a newer sample from the old one, * wait until newer data are available. */ if (sys_poll < sys_peer->minpoll) sys_poll = sys_peer->minpoll; if (sys_poll > sys_peer->maxpoll) sys_poll = sys_peer->maxpoll; poll_update(sys_peer, sys_poll); if (sys_peer->epoch <= sys_clocktime) return; #ifdef DEBUG if (debug) printf("clock_update: at %ld assoc %d \n", current_time, peer_associations); #endif oleap = sys_leap; ostratum = sys_stratum; switch (local_clock(sys_peer, sys_offset)) { /* * Clock exceeds panic threshold. Life as we know it ends. */ case -1: report_event(EVNT_SYSFAULT, NULL); exit (-1); /* not reached */ /* * Clock was stepped. Flush all time values of all peers. */ case 2: clear_all(); sys_leap = LEAP_NOTINSYNC; sys_stratum = STRATUM_UNSPEC; sys_peer = NULL; sys_rootdelay = 0; sys_rootdispersion = 0; memcpy(&sys_refid, "STEP", 4); report_event(EVNT_CLOCKRESET, NULL); break; /* * Clock was slewed. Update the system stratum, leap bits, root * delay, root dispersion, reference ID and reference time. If * the leap changes, we gotta reroll the keys. Except for * reference clocks, the minimum dispersion increment is not * less than sys_mindisp. */ case 1: sys_leap = leap_next; sys_stratum = min(sys_peer->stratum + 1, STRATUM_UNSPEC); sys_reftime = sys_peer->rec; /* * In orphan mode the stratum defaults to the orphan * stratum. The root delay is set to a random value * generated at startup. The root dispersion is set from * the peer dispersion; the peer root dispersion is * ignored. */ dtemp = sys_peer->disp + clock_phi * (current_time - sys_peer->update) + sys_jitter + fabs(sys_peer->offset); #ifdef REFCLOCK if (!(sys_peer->flags & FLAG_REFCLOCK) && dtemp < sys_mindisp) dtemp = sys_mindisp; #else if (dtemp < sys_mindisp) dtemp = sys_mindisp; #endif /* REFCLOCK */ if (sys_stratum >= sys_orphan) { sys_stratum = sys_orphan; sys_rootdelay = sys_peer->delay; sys_rootdispersion = dtemp; } else { sys_rootdelay = sys_peer->delay + sys_peer->rootdelay; sys_rootdispersion = dtemp + sys_peer->rootdispersion; } if (oleap == LEAP_NOTINSYNC) { report_event(EVNT_SYNCCHG, NULL); #ifdef OPENSSL expire_all(); crypto_update(); #endif /* OPENSSL */ } break; /* * Popcorn spike or step threshold exceeded. Pretend it never * happened. */ default: break; } if (ostratum != sys_stratum) report_event(EVNT_PEERSTCHG, NULL); } /* * poll_update - update peer poll interval */ void poll_update( struct peer *peer, int mpoll ) { int hpoll; /* * This routine figures out when the next poll should be sent. * That turns out to be wickedly complicated. The big problem is * that sometimes the time for the next poll is in the past. * Watch out for races here between the receive process and the * poll process. The key assertion is that, if nextdate equals * current_time, the call is from the poll process; otherwise, * it is from the receive process. * * First, bracket the poll interval according to the type of * association and options. If a fixed interval is configured, * use minpoll. This primarily is for reference clocks, but * works for any association. */ if (peer->flags & FLAG_FIXPOLL) { hpoll = peer->minpoll; /* * The ordinary case; clamp the poll interval between minpoll * and maxpoll. */ } else { hpoll = max(min(peer->maxpoll, mpoll), peer->minpoll); } #ifdef OPENSSL /* * Bit of crass arrogance at this point. If the poll interval * has changed and we have a keylist, the lifetimes in the * keylist are probably bogus. In this case purge the keylist * and regenerate it later. */ if (hpoll != peer->hpoll) key_expire(peer); #endif /* OPENSSL */ peer->hpoll = hpoll; /* * Now we figure out if there is an override. If during the * crypto protocol and a message is pending, make it wait not * more than two seconds. */ #ifdef OPENSSL if (peer->cmmd != NULL && (sys_leap != LEAP_NOTINSYNC || peer->crypto)) { peer->nextdate = current_time + RESP_DELAY; /* * If we get called from the receive routine while a burst is * pending, just slink away. If from the poll routine and a * reference clock or a pending crypto response, delay for one * second. If this is the first sent in a burst, wait for the * modem to come up. For others in the burst, delay two seconds. */ } else if (peer->burst > 0) { #else /* OPENSSL */ if (peer->burst > 0) { #endif /* OPENSSL */ if (peer->nextdate != current_time) return; #ifdef REFCLOCK else if (peer->flags & FLAG_REFCLOCK) peer->nextdate += RESP_DELAY; #endif /* REFCLOCK */ else if (peer->flags & (FLAG_IBURST | FLAG_BURST) && peer->burst == NTP_BURST) peer->nextdate += sys_calldelay; else peer->nextdate += BURST_DELAY; /* * The ordinary case; use the minimum of the host and peer * intervals, but not less than minpoll. In other words, * oversampling is okay but understampling is evil. */ } else { peer->nextdate = peer->outdate + RANDPOLL(max(min(peer->ppoll, hpoll), peer->minpoll)); } /* * If the time for the next poll has already happened, bring it * up to the next second after this one. This way the only way * to get nexdate == current time is from the poll routine. */ if (peer->nextdate <= current_time) peer->nextdate = current_time + 1; #ifdef DEBUG if (debug > 1) printf("poll_update: at %lu %s flags %04x poll %d burst %d last %lu next %lu\n", current_time, ntoa(&peer->srcadr), peer->flags, peer->hpoll, peer->burst, peer->outdate, peer->nextdate); #endif } /* * peer_crypto_clear - discard crypto information */ void peer_crypto_clear( struct peer *peer ) { /* * If cryptographic credentials have been acquired, toss them to * Valhalla. Note that autokeys are ephemeral, in that they are * tossed immediately upon use. Therefore, the keylist can be * purged anytime without needing to preserve random keys. Note * that, if the peer is purged, the cryptographic variables are * purged, too. This makes it much harder to sneak in some * unauthenticated data in the clock filter. */ DPRINTF(1, ("peer_crypto_clear: at %ld next %ld assoc ID %d\n", current_time, peer->nextdate, peer->associd)); #ifdef OPENSSL peer->assoc = 0; peer->crypto = 0; if (peer->pkey != NULL) EVP_PKEY_free(peer->pkey); peer->pkey = NULL; peer->digest = NULL; /* XXX MEMLEAK? check whether this needs to be freed in any way - never was freed */ if (peer->subject != NULL) free(peer->subject); peer->subject = NULL; if (peer->issuer != NULL) free(peer->issuer); peer->issuer = NULL; peer->pkeyid = 0; peer->pcookie = 0; if (peer->ident_pkey != NULL) EVP_PKEY_free(peer->ident_pkey); peer->ident_pkey = NULL; memset(&peer->fstamp, 0, sizeof(peer->fstamp)); if (peer->iffval != NULL) BN_free(peer->iffval); peer->iffval = NULL; if (peer->grpkey != NULL) BN_free(peer->grpkey); peer->grpkey = NULL; value_free(&peer->cookval); value_free(&peer->recval); if (peer->cmmd != NULL) { free(peer->cmmd); peer->cmmd = NULL; } key_expire(peer); value_free(&peer->encrypt); #endif /* OPENSSL */ } /* * peer_clear - clear peer filter registers. See Section 3.4.8 of the spec. */ void peer_clear( struct peer *peer, /* peer structure */ char *ident /* tally lights */ ) { int i; peer_crypto_clear(peer); if (peer == sys_peer) sys_peer = NULL; /* * Wipe the association clean and initialize the nonzero values. */ memset(CLEAR_TO_ZERO(peer), 0, LEN_CLEAR_TO_ZERO); peer->estbdelay = sys_bdelay; peer->ppoll = peer->maxpoll; peer->hpoll = peer->minpoll; peer->disp = MAXDISPERSE; peer->jitter = LOGTOD(sys_precision); for (i = 0; i < NTP_SHIFT; i++) { peer->filter_order[i] = i; peer->filter_disp[i] = MAXDISPERSE; } #ifdef REFCLOCK if (!(peer->flags & FLAG_REFCLOCK)) { peer->leap = LEAP_NOTINSYNC; peer->stratum = STRATUM_UNSPEC; memcpy(&peer->refid, ident, 4); } #else peer->leap = LEAP_NOTINSYNC; peer->stratum = STRATUM_UNSPEC; memcpy(&peer->refid, ident, 4); #endif /* REFCLOCK */ /* * During initialization use the association count to spread out * the polls at one-second intervals. Othersie, randomize over * the minimum poll interval in order to avoid broadcast * implosion. */ peer->nextdate = peer->update = peer->outdate = current_time; if (initializing) peer->nextdate += peer_associations; else if (peer->hmode == MODE_PASSIVE) peer->nextdate += RESP_DELAY; else peer->nextdate += (ntp_random() & ((1 << NTP_MINDPOLL) - 1)); DPRINTF(1, ("peer_clear: at %ld next %ld assoc ID %d refid %s\n", current_time, peer->nextdate, peer->associd, ident)); } /* * clock_filter - add incoming clock sample to filter register and run * the filter procedure to find the best sample. */ void clock_filter( struct peer *peer, /* peer structure pointer */ double sample_offset, /* clock offset */ double sample_delay, /* roundtrip delay */ double sample_disp /* dispersion */ ) { double dst[NTP_SHIFT]; /* distance vector */ int ord[NTP_SHIFT]; /* index vector */ int i, j, k, m; double dtemp, etemp; /* * Shift the new sample into the register and discard the oldest * one. The new offset and delay come directly from the * timestamp calculations. The dispersion grows from the last * outbound packet or reference clock update to the present time * and increased by the sum of the peer precision and the system * precision. The delay can sometimes swing negative due to * frequency skew, so it is clamped non-negative. */ j = peer->filter_nextpt; peer->filter_offset[j] = sample_offset; peer->filter_delay[j] = max(0, sample_delay); peer->filter_disp[j] = sample_disp; peer->filter_epoch[j] = current_time; j = (j + 1) % NTP_SHIFT; peer->filter_nextpt = j; /* * Update dispersions since the last update and at the same * time initialize the distance and index lists. The distance * list uses a compound metric. If the sample is valid and * younger than the minimum Allan intercept, use delay; * otherwise, use biased dispersion. */ dtemp = clock_phi * (current_time - peer->update); peer->update = current_time; for (i = NTP_SHIFT - 1; i >= 0; i--) { if (i != 0) peer->filter_disp[j] += dtemp; if (peer->filter_disp[j] >= MAXDISPERSE) peer->filter_disp[j] = MAXDISPERSE; if (peer->filter_disp[j] >= MAXDISPERSE) dst[i] = MAXDISPERSE; else if (peer->update - peer->filter_epoch[j] > allan_xpt) dst[i] = sys_maxdist + peer->filter_disp[j]; else dst[i] = peer->filter_delay[j]; ord[i] = j; j++; j %= NTP_SHIFT; } /* * If the clock discipline has stabilized, sort the samples in * both lists by distance. Note, we do not displace a higher * distance sample by a lower distance one unless lower by at * least the precision. */ if (state == 4) { for (i = 1; i < NTP_SHIFT; i++) { for (j = 0; j < i; j++) { if (dst[j] > dst[i] + LOGTOD(sys_precision)) { k = ord[j]; ord[j] = ord[i]; ord[i] = k; etemp = dst[j]; dst[j] = dst[i]; dst[i] = etemp; } } } } /* * Copy the index list to the association structure so ntpq * can see it later. Prune the distance list to samples less * than max distance, but keep at least two valid samples for * jitter calculation. */ m = 0; for (i = 0; i < NTP_SHIFT; i++) { peer->filter_order[i] = (u_char) ord[i]; if (dst[i] >= MAXDISPERSE || (m >= 2 && dst[i] >= sys_maxdist)) continue; m++; } /* * Compute the dispersion and jitter. The dispersion is weighted * exponentially by NTP_FWEIGHT (0.5) so it is normalized close * to 1.0. The jitter is the RMS differences relative to the * lowest delay sample. If no acceptable samples remain in the * shift register, quietly tiptoe home leaving only the * dispersion. */ peer->disp = peer->jitter = 0; k = ord[0]; for (i = NTP_SHIFT - 1; i >= 0; i--) { j = ord[i]; peer->disp = NTP_FWEIGHT * (peer->disp + peer->filter_disp[j]); if (i < m) peer->jitter += DIFF(peer->filter_offset[j], peer->filter_offset[k]); } /* * If no acceptable samples remain in the shift register, * quietly tiptoe home leaving only the dispersion. Otherwise, * save the offset, delay and jitter. Note the jitter must not * be less than the precision. */ if (m == 0) return; etemp = fabs(peer->offset - peer->filter_offset[k]); peer->offset = peer->filter_offset[k]; peer->delay = peer->filter_delay[k]; if (m > 1) peer->jitter /= m - 1; peer->jitter = max(SQRT(peer->jitter), LOGTOD(sys_precision)); /* * A new sample is useful only if it is younger than the last * one used. Note the order is FIFO if the clock discipline has * not stabilized. */ if (peer->filter_epoch[k] <= peer->epoch) { #ifdef DEBUG if (debug) printf("clock_filter: discard %lu\n", peer->epoch - peer->filter_epoch[k]); #endif return; } /* * If the difference between the last offset and the current one * exceeds the jitter by CLOCK_SGATE and the interval since the * last update is less than twice the system poll interval, * consider the update a popcorn spike and ignore it. */ if (etemp > CLOCK_SGATE * peer->jitter && m > 1 && peer->filter_epoch[k] - peer->epoch < 2. * ULOGTOD(sys_poll)) { #ifdef DEBUG if (debug) printf("clock_filter: popcorn %.6f %.6f\n", etemp, dtemp); #endif return; } /* * The mitigated sample statistics are saved for later * processing. If not in a burst, tickle the select. */ peer->epoch = peer->filter_epoch[k]; #ifdef DEBUG if (debug) printf( "clock_filter: n %d off %.6f del %.6f dsp %.6f jit %.6f, age %lu\n", m, peer->offset, peer->delay, peer->disp, peer->jitter, current_time - peer->epoch); #endif if (peer->burst == 0 || sys_leap == LEAP_NOTINSYNC) clock_select(); } /* * clock_select - find the pick-of-the-litter clock * * LOCKCLOCK: If the local clock is the prefer peer, it will always be * enabled, even if declared falseticker, (2) only the prefer peer can * be selected as the system peer, (3) if the external source is down, * the system leap bits are set to 11 and the stratum set to infinity. */ void clock_select(void) { struct peer *peer; int i, j, k, n; int nlist, nl3; int allow, osurv; double d, e, f, g; double high, low; double synch[NTP_MAXASSOC], error[NTP_MAXASSOC]; struct peer *osys_peer; struct peer *typeacts = NULL; struct peer *typelocal = NULL; struct peer *typesystem = NULL; static int list_alloc = 0; static struct endpoint *endpoint = NULL; static int *indx = NULL; static struct peer **peer_list = NULL; static u_int endpoint_size = 0; static u_int indx_size = 0; static u_int peer_list_size = 0; /* * Initialize and create endpoint, index and peer lists big * enough to handle all associations. */ osys_peer = sys_peer; sys_peer = NULL; sys_pps = NULL; sys_prefer = NULL; osurv = sys_survivors; sys_survivors = 0; #ifdef LOCKCLOCK sys_leap = LEAP_NOTINSYNC; sys_stratum = STRATUM_UNSPEC; memcpy(&sys_refid, "DOWN", 4); #endif /* LOCKCLOCK */ nlist = 0; for (n = 0; n < NTP_HASH_SIZE; n++) nlist += peer_hash_count[n]; if (nlist > list_alloc) { if (list_alloc > 0) { free(endpoint); free(indx); free(peer_list); } while (list_alloc < nlist) { list_alloc += 5; endpoint_size += 5 * 3 * sizeof(*endpoint); indx_size += 5 * 3 * sizeof(*indx); peer_list_size += 5 * sizeof(*peer_list); } endpoint = (struct endpoint *)emalloc(endpoint_size); indx = (int *)emalloc(indx_size); peer_list = (struct peer **)emalloc(peer_list_size); } /* * Initially, we populate the island with all the rifraff peers * that happen to be lying around. Those with seriously * defective clocks are immediately booted off the island. Then, * the falsetickers are culled and put to sea. The truechimers * remaining are subject to repeated rounds where the most * unpopular at each round is kicked off. When the population * has dwindled to sys_minclock, the survivors split a million * bucks and collectively crank the chimes. */ nlist = nl3 = 0; /* none yet */ for (n = 0; n < NTP_HASH_SIZE; n++) { for (peer = peer_hash[n]; peer != NULL; peer = peer->next) { peer->flags &= ~FLAG_SYSPEER; peer->status = CTL_PST_SEL_REJECT; /* * Leave the island immediately if the peer is * unfit to synchronize. */ if (peer_unfit(peer)) continue; /* * Don't allow the local clock or modem drivers * in the kitchen at this point, unless the * prefer peer. Do that later, but only if * nobody else is around. These guys are all * configured, so we never throw them away. */ #ifdef REFCLOCK if (peer->refclktype == REFCLK_LOCALCLOCK #if defined(VMS) && defined(VMS_LOCALUNIT) /* wjm: VMS_LOCALUNIT taken seriously */ && REFCLOCKUNIT(&peer->srcadr) != VMS_LOCALUNIT #endif /* VMS && VMS_LOCALUNIT */ ) { typelocal = peer; #ifndef LOCKCLOCK if (!(peer->flags & FLAG_PREFER)) continue; /* no local clock */ #endif /* LOCKCLOCK */ } if (peer->sstclktype == CTL_SST_TS_TELEPHONE) { typeacts = peer; if (!(peer->flags & FLAG_PREFER)) continue; /* no acts */ } #endif /* REFCLOCK */ /* * If we get this far, the peer can stay on the * island, but does not yet have the immunity * idol. */ peer->status = CTL_PST_SEL_SANE; peer_list[nlist++] = peer; /* * Insert each interval endpoint on the sorted * list. */ e = peer->offset; /* Upper end */ f = root_distance(peer); e = e + f; for (i = nl3 - 1; i >= 0; i--) { if (e >= endpoint[indx[i]].val) break; indx[i + 3] = indx[i]; } indx[i + 3] = nl3; endpoint[nl3].type = 1; endpoint[nl3++].val = e; e = e - f; /* Center point */ for (; i >= 0; i--) { if (e >= endpoint[indx[i]].val) break; indx[i + 2] = indx[i]; } indx[i + 2] = nl3; endpoint[nl3].type = 0; endpoint[nl3++].val = e; e = e - f; /* Lower end */ for (; i >= 0; i--) { if (e >= endpoint[indx[i]].val) break; indx[i + 1] = indx[i]; } indx[i + 1] = nl3; endpoint[nl3].type = -1; endpoint[nl3++].val = e; } } #ifdef DEBUG if (debug > 2) for (i = 0; i < nl3; i++) printf("select: endpoint %2d %.6f\n", endpoint[indx[i]].type, endpoint[indx[i]].val); #endif /* * This is the actual algorithm that cleaves the truechimers * from the falsetickers. The original algorithm was described * in Keith Marzullo's dissertation, but has been modified for * better accuracy. * * Briefly put, we first assume there are no falsetickers, then * scan the candidate list first from the low end upwards and * then from the high end downwards. The scans stop when the * number of intersections equals the number of candidates less * the number of falsetickers. If this doesn't happen for a * given number of falsetickers, we bump the number of * falsetickers and try again. If the number of falsetickers * becomes equal to or greater than half the number of * candidates, the Albanians have won the Byzantine wars and * correct synchronization is not possible. * * Here, nlist is the number of candidates and allow is the * number of falsetickers. Upon exit, the truechimers are the * susvivors with offsets not less than low and not greater than * high. There may be none of them. */ low = 1e9; high = -1e9; for (allow = 0; 2 * allow < nlist; allow++) { int found; /* * Bound the interval (low, high) as the largest * interval containing points from presumed truechimers. */ found = 0; n = 0; for (i = 0; i < nl3; i++) { low = endpoint[indx[i]].val; n -= endpoint[indx[i]].type; if (n >= nlist - allow) break; if (endpoint[indx[i]].type == 0) found++; } n = 0; for (j = nl3 - 1; j >= 0; j--) { high = endpoint[indx[j]].val; n += endpoint[indx[j]].type; if (n >= nlist - allow) break; if (endpoint[indx[j]].type == 0) found++; } /* * If the number of candidates found outside the * interval is greater than the number of falsetickers, * then at least one truechimer is outside the interval, * so go around again. This is what makes this algorithm * different than Marzullo's. */ if (found > allow) continue; /* * If an interval containing truechimers is found, stop. * If not, increase the number of falsetickers and go * around again. */ if (high > low) break; } /* * Clustering algorithm. Construct candidate list in order first * by stratum then by root distance, but keep only the best * NTP_MAXASSOC of them. Scan the list to find falsetickers, who * leave the island immediately. The TRUE peer is always a * truechimer. We must leave at least one peer to collect the * million bucks. If in orphan mode, rascals found with lower * stratum are guaranteed a seat on the bus. */ j = 0; for (i = 0; i < nlist; i++) { peer = peer_list[i]; if (nlist > 1 && (peer->offset <= low || peer->offset >= high) && !(peer->flags & FLAG_TRUE) && !(sys_stratum >= sys_orphan && peer->stratum < sys_orphan)) continue; peer->status = CTL_PST_SEL_DISTSYSPEER; /* * The order metric is formed from the stratum times * max distance (1.) plus the root distance. It strongly * favors the lowest stratum, but a higher stratum peer * can capture the clock if the low stratum dominant * hasn't been heard for awhile. */ d = root_distance(peer) + peer->stratum * sys_maxdist; if (j >= NTP_MAXASSOC) { if (d >= synch[j - 1]) continue; else j--; } for (k = j; k > 0; k--) { if (d >= synch[k - 1]) break; peer_list[k] = peer_list[k - 1]; error[k] = error[k - 1]; synch[k] = synch[k - 1]; } peer_list[k] = peer; error[k] = peer->jitter; synch[k] = d; j++; } nlist = j; /* * If no survivors remain at this point, check if the local * clock or modem drivers have been found. If so, nominate one * of them as the only survivor. Otherwise, give up and leave * the island to the rats. */ if (nlist == 0) { if (typeacts != 0) { typeacts->status = CTL_PST_SEL_DISTSYSPEER; peer_list[0] = typeacts; nlist = 1; } else if (typelocal != 0) { typelocal->status = CTL_PST_SEL_DISTSYSPEER; peer_list[0] = typelocal; nlist = 1; } else { if (osys_peer != NULL) { NLOG(NLOG_SYNCSTATUS) msyslog(LOG_INFO, "no servers reachable"); report_event(EVNT_PEERSTCHG, NULL); } } } /* * We can only trust the survivors if the number of candidates * sys_minsane is at least the number required to detect and * cast out one falsticker. For the Byzantine agreement * algorithm used here, that number is 4; however, the default * sys_minsane is 1 to speed initial synchronization. Careful * operators will tinker a higher value and use at least that * number of synchronization sources. */ if (nlist < sys_minsane) return; for (i = 0; i < nlist; i++) peer_list[i]->status = CTL_PST_SEL_SELCAND; /* * Now, vote outlyers off the island by select jitter weighted * by root distance. Continue voting as long as there are more * than sys_minclock survivors and the minimum select jitter is * greater than the maximum peer jitter. Stop if we are about to * discard a TRUE or PREFER peer, who of course has the * immunity idol. */ while (1) { d = 1e9; e = -1e9; f = g = 0; k = 0; for (i = 0; i < nlist; i++) { if (error[i] < d) d = error[i]; f = 0; if (nlist > 1) { for (j = 0; j < nlist; j++) f += DIFF(peer_list[j]->offset, peer_list[i]->offset); f = SQRT(f / (nlist - 1)); } if (f * synch[i] > e) { g = f; e = f * synch[i]; k = i; } } f = max(f, LOGTOD(sys_precision)); if (nlist <= sys_minclock || f <= d || peer_list[k]->flags & (FLAG_TRUE | FLAG_PREFER)) break; #ifdef DEBUG if (debug > 2) printf( "select: drop %s select %.6f jitter %.6f\n", ntoa(&peer_list[k]->srcadr), g, d); #endif for (j = k + 1; j < nlist; j++) { peer_list[j - 1] = peer_list[j]; error[j - 1] = error[j]; } nlist--; } /* * What remains is a list usually not greater than sys_minclock * peers. We want only a peer at the lowest stratum to become * the system peer, although all survivors are eligible for the * combining algorithm. Consider each peer in turn and OR the * leap bits on the assumption that, if some of them honk * nonzero bits, they must know what they are doing. Check for * prefer and pps peers at any stratum. Note that the head of * the list is at the lowest stratum and that unsynchronized * peers cannot survive this far. */ leap_next = 0; for (i = 0; i < nlist; i++) { peer = peer_list[i]; sys_survivors++; leap_next |= peer->leap; peer->status = CTL_PST_SEL_SYNCCAND; if (peer->flags & FLAG_PREFER) sys_prefer = peer; if (peer == osys_peer) typesystem = peer; #ifdef REFCLOCK if (peer->refclktype == REFCLK_ATOM_PPS) sys_pps = peer; #endif /* REFCLOCK */ #if DEBUG if (debug > 1) printf("cluster: survivor %s metric %.6f\n", ntoa(&peer_list[i]->srcadr), synch[i]); #endif } /* * Anticlockhop provision. Keep the current system peer if it is * a survivor but not first in the list. But do that only HOPPER * times. */ if (osys_peer == NULL || typesystem == NULL || typesystem == peer_list[0] || sys_hopper > sys_maxhop) { typesystem = peer_list[0]; sys_hopper = 0; } else { peer->selbroken++; } /* * Mitigation rules of the game. There are several types of * peers that can be selected here: (1) orphan, (2) prefer peer * (flag FLAG_PREFER) (3) pps peers (type REFCLK_ATOM_PPS), (4) * the existing system peer, if any, and (5) the head of the * survivor list. */ if (typesystem->stratum >= sys_orphan) { /* * If in orphan mode, choose the system peer. If the * lowest distance, we are the orphan parent and the * offset is zero. */ sys_peer = typesystem; sys_peer->status = CTL_PST_SEL_SYSPEER; if (sys_orphandelay < sys_peer->rootdelay) { sys_offset = 0; sys_refid = htonl(LOOPBACKADR); } else { sys_offset = sys_peer->offset; sys_refid = addr2refid(&sys_peer->srcadr); } sys_jitter = LOGTOD(sys_precision); #ifdef DEBUG if (debug > 1) printf("select: orphan offset %.6f\n", sys_offset); #endif } else if (sys_prefer) { /* * If a pps peer is present, choose it; otherwise, * choose the prefer peer. */ if (sys_pps) { sys_peer = sys_pps; sys_peer->status = CTL_PST_SEL_PPS; sys_offset = sys_peer->offset; if (!pps_control) NLOG(NLOG_SYSEVENT) msyslog(LOG_INFO, "pps sync enabled"); pps_control = current_time; #ifdef DEBUG if (debug > 1) printf("select: pps offset %.6f\n", sys_offset); #endif } else { sys_peer = sys_prefer; sys_peer->status = CTL_PST_SEL_SYSPEER; sys_offset = sys_peer->offset; #ifdef DEBUG if (debug > 1) printf("select: prefer offset %.6f\n", sys_offset); #endif } if (sys_peer->stratum == STRATUM_REFCLOCK || sys_peer->stratum == STRATUM_UNSPEC) sys_refid = sys_peer->refid; else sys_refid = addr2refid(&sys_peer->srcadr); sys_jitter = sys_peer->jitter; } else { /* * Otherwise, choose the anticlockhopper. */ sys_peer = typesystem; sys_peer->status = CTL_PST_SEL_SYSPEER; clock_combine(peer_list, nlist); if (sys_peer->stratum == STRATUM_REFCLOCK || sys_peer->stratum == STRATUM_UNSPEC) sys_refid = sys_peer->refid; else sys_refid = addr2refid(&sys_peer->srcadr); sys_jitter = SQRT(SQUARE(sys_peer->jitter) + SQUARE(sys_jitter)); #ifdef DEBUG if (debug > 1) printf("select: combine offset %.6f\n", sys_offset); #endif } /* * We have found the alpha male. */ sys_peer->flags |= FLAG_SYSPEER; if (osys_peer != sys_peer) { char *src; report_event(EVNT_PEERSTCHG, NULL); #ifdef REFCLOCK if (sys_peer->flags & FLAG_REFCLOCK) src = refnumtoa(&sys_peer->srcadr); else #endif /* REFCLOCK */ src = ntoa(&sys_peer->srcadr); NLOG(NLOG_SYNCSTATUS) msyslog(LOG_INFO, "synchronized to %s, stratum %d", src, sys_peer->stratum); } clock_update(); } /* * clock_combine - compute system offset and jitter from selected peers */ static void clock_combine( struct peer **peers, /* survivor list */ int npeers /* number of survivors */ ) { int i; double x, y, z, w; y = z = w = 0; for (i = 0; i < npeers; i++) { x = root_distance(peers[i]); y += 1. / x; z += peers[i]->offset / x; w += SQUARE(peers[i]->offset - peers[0]->offset) / x; } sys_offset = z / y; sys_jitter = SQRT(w / y); } /* * root_distance - compute synchronization distance from peer to root */ static double root_distance( struct peer *peer ) { double dist; /* * Careful squeak here. The value returned must be greater than * the minimum root dispersion in order to avoid clockhop with * highly precise reference clocks. In orphan mode lose the peer * root delay, as that is used by the election algorithm. */ if (peer->stratum >= sys_orphan) dist = 0; else dist = peer->rootdelay; dist += max(sys_mindisp, dist + peer->delay) / 2 + peer->rootdispersion + peer->disp + clock_phi * (current_time - peer->update) + peer->jitter; return (dist); } /* * peer_xmit - send packet for persistent association. */ static void peer_xmit( struct peer *peer /* peer structure pointer */ ) { struct pkt xpkt; /* transmit packet */ int sendlen, authlen; keyid_t xkeyid = 0; /* transmit key ID */ l_fp xmt_tx; if (!peer->dstadr) /* don't bother with peers without interface */ return; /* * This is deliciously complicated. There are three cases. * * case leap stratum refid delay dispersion * * normal system system system system system * orphan child 00 orphan system orphan system * orphan parent 00 orphan loopbk 0 0 */ /* * This is a normal packet. Use the system variables. */ if (sys_stratum < sys_orphan) { xpkt.li_vn_mode = PKT_LI_VN_MODE(sys_leap, peer->version, peer->hmode); xpkt.stratum = STRATUM_TO_PKT(sys_stratum); xpkt.refid = sys_refid; xpkt.rootdelay = HTONS_FP(DTOFP(sys_rootdelay)); xpkt.rootdispersion = HTONS_FP(DTOUFP(sys_rootdispersion)); /* * This is a orphan child packet. The host is synchronized to an * orphan parent. Show leap synchronized, orphan stratum, system * reference ID, orphan root delay and system root dispersion. */ } else if (sys_peer != NULL) { xpkt.li_vn_mode = PKT_LI_VN_MODE(LEAP_NOWARNING, peer->version, peer->hmode); xpkt.stratum = STRATUM_TO_PKT(sys_orphan); xpkt.refid = htonl(LOOPBACKADR); xpkt.rootdelay = HTONS_FP(DTOFP(sys_orphandelay)); xpkt.rootdispersion = HTONS_FP(DTOUFP(sys_rootdispersion)); /* * This is an orphan parent. Show leap synchronized, orphan * stratum, loopack reference ID and zero root delay and root * dispersion. */ } else { xpkt.li_vn_mode = PKT_LI_VN_MODE(LEAP_NOWARNING, peer->version, peer->hmode); xpkt.stratum = STRATUM_TO_PKT(sys_orphan); xpkt.refid = sys_refid; xpkt.rootdelay = 0; xpkt.rootdispersion = 0; } xpkt.ppoll = peer->hpoll; xpkt.precision = sys_precision; HTONL_FP(&sys_reftime, &xpkt.reftime); HTONL_FP(&peer->org, &xpkt.org); HTONL_FP(&peer->rec, &xpkt.rec); /* * If the received packet contains a MAC, the transmitted packet * is authenticated and contains a MAC. If not, the transmitted * packet is not authenticated. * * It is most important when autokey is in use that the local * interface IP address be known before the first packet is * sent. Otherwise, it is not possible to compute a correct MAC * the recipient will accept. Thus, the I/O semantics have to do * a little more work. In particular, the wildcard interface * might not be usable. */ sendlen = LEN_PKT_NOMAC; if (!(peer->flags & FLAG_AUTHENABLE)) { get_systime(&peer->xmt); HTONL_FP(&peer->xmt, &xpkt.xmt); sendpkt(&peer->srcadr, peer->dstadr, sys_ttl[peer->ttl], &xpkt, sendlen); peer->sent++; #ifdef DEBUG if (debug) printf("transmit: at %ld %s->%s mode %d\n", current_time, peer->dstadr ? stoa(&peer->dstadr->sin) : "-", stoa(&peer->srcadr), peer->hmode); #endif return; } /* * The received packet contains a MAC, so the transmitted packet * must be authenticated. If autokey is enabled, fuss with the * various modes; otherwise, symmetric key cryptography is used. */ #ifdef OPENSSL if (crypto_flags && (peer->flags & FLAG_SKEY)) { struct exten *exten; /* extension field */ /* * The Public Key Dance (PKD): Cryptographic credentials * are contained in extension fields, each including a * 4-octet length/code word followed by a 4-octet * association ID and optional additional data. Optional * data includes a 4-octet data length field followed by * the data itself. Request messages are sent from a * configured association; response messages can be sent * from a configured association or can take the fast * path without ever matching an association. Response * messages have the same code as the request, but have * a response bit and possibly an error bit set. In this * implementation, a message may contain no more than * one command and no more than one response. * * Cryptographic session keys include both a public and * a private componet. Request and response messages * using extension fields are always sent with the * private component set to zero. Packets without * extension fields indlude the private component when * the session key is generated. */ while (1) { /* * Allocate and initialize a keylist if not * already done. Then, use the list in inverse * order, discarding keys once used. Keep the * latest key around until the next one, so * clients can use client/server packets to * compute propagation delay. * * Note that once a key is used from the list, * it is retained in the key cache until the * next key is used. This is to allow a client * to retrieve the encrypted session key * identifier to verify authenticity. * * If for some reason a key is no longer in the * key cache, a birthday has happened and the * pseudo-random sequence is probably broken. In * that case, purge the keylist and regenerate * it. */ if (peer->keynumber == 0) make_keylist(peer, peer->dstadr); else peer->keynumber--; xkeyid = peer->keylist[peer->keynumber]; if (authistrusted(xkeyid)) break; else key_expire(peer); } peer->keyid = xkeyid; exten = NULL; switch (peer->hmode) { /* * In broadcast server mode the autokey values are * required by the broadcast clients. Push them when a * new keylist is generated; otherwise, push the * association message so the client can request them at * other times. */ case MODE_BROADCAST: if (peer->flags & FLAG_ASSOC) exten = crypto_args(peer, CRYPTO_AUTO | CRYPTO_RESP, NULL); else exten = crypto_args(peer, CRYPTO_ASSOC | CRYPTO_RESP, NULL); break; /* * In symmetric modes the digest, certificate, agreement * parameters, cookie and autokey values are required. * The leapsecond table is optional. But, a passive peer * will not believe the active peer until the latter has * synchronized, so the agreement must be postponed * until then. In any case, if a new keylist is * generated, the autokey values are pushed. * * If the crypto bit is lit, don't send requests. */ case MODE_ACTIVE: case MODE_PASSIVE: if (peer->flash & TEST9) break; /* * Parameter and certificate. */ if (!peer->crypto) exten = crypto_args(peer, CRYPTO_ASSOC, sys_hostname); else if (!(peer->crypto & CRYPTO_FLAG_VALID)) exten = crypto_args(peer, CRYPTO_CERT, peer->issuer); /* * Identity. Note we have to sign the * certificate before the cookie to avoid a * deadlock when the passive peer is walking the * certificate trail. Awesome. */ else if (!(peer->crypto & CRYPTO_FLAG_VRFY)) exten = crypto_args(peer, crypto_ident(peer), NULL); else if (sys_leap != LEAP_NOTINSYNC && !(peer->crypto & CRYPTO_FLAG_SIGN)) exten = crypto_args(peer, CRYPTO_SIGN, sys_hostname); /* * Autokey. We request the cookie only when the * server and client are synchronized and * signatures work both ways. On the other hand, * the active peer needs the autokey values * before then and when the passive peer is * waiting for the active peer to synchronize. * Any time we regenerate the key list, we offer * the autokey values without being asked. */ else if (sys_leap != LEAP_NOTINSYNC && peer->leap != LEAP_NOTINSYNC && !(peer->crypto & CRYPTO_FLAG_AGREE)) exten = crypto_args(peer, CRYPTO_COOK, NULL); else if (peer->flags & FLAG_ASSOC) exten = crypto_args(peer, CRYPTO_AUTO | CRYPTO_RESP, NULL); else if (!(peer->crypto & CRYPTO_FLAG_AUTO)) exten = crypto_args(peer, CRYPTO_AUTO, NULL); /* * Postamble. We trade leapseconds only when the * server and client are synchronized. */ else if (sys_leap != LEAP_NOTINSYNC && peer->leap != LEAP_NOTINSYNC && peer->crypto & CRYPTO_FLAG_TAI && !(peer->crypto & CRYPTO_FLAG_LEAP)) exten = crypto_args(peer, CRYPTO_TAI, NULL); break; /* * In client mode the digest, certificate, agreement * parameters and cookie are required. The leapsecond * table is optional. If broadcast client mode, the * autokey values are required as well. In broadcast * client mode, these values must be acquired during the * client/server exchange to avoid having to wait until * the next key list regeneration. Otherwise, the poor * dude may die a lingering death until becoming * unreachable and attempting rebirth. * * If neither the server or client have the agreement * parameters, the protocol transmits the cookie in the * clear. If the server has the parameters, the client * requests them and the protocol blinds it using the * agreed key. It is a protocol error if the client has * the parameters but the server does not. * * If the crypto bit is lit, don't send requests. */ case MODE_CLIENT: if (peer->flash & TEST9) break; /* * Parameter and certificate. */ if (!peer->crypto) exten = crypto_args(peer, CRYPTO_ASSOC, sys_hostname); else if (!(peer->crypto & CRYPTO_FLAG_VALID)) exten = crypto_args(peer, CRYPTO_CERT, peer->issuer); /* * Identity */ else if (!(peer->crypto & CRYPTO_FLAG_VRFY)) exten = crypto_args(peer, crypto_ident(peer), NULL); /* * Autokey */ else if (!(peer->crypto & CRYPTO_FLAG_AGREE)) exten = crypto_args(peer, CRYPTO_COOK, NULL); else if (!(peer->crypto & CRYPTO_FLAG_AUTO) && (peer->cast_flags & MDF_BCLNT)) exten = crypto_args(peer, CRYPTO_AUTO, NULL); /* * Postamble. We can sign the certificate here, * since there is no chance of deadlock. */ else if (sys_leap != LEAP_NOTINSYNC && !(peer->crypto & CRYPTO_FLAG_SIGN)) exten = crypto_args(peer, CRYPTO_SIGN, sys_hostname); else if (sys_leap != LEAP_NOTINSYNC && peer->crypto & CRYPTO_FLAG_TAI && !(peer->crypto & CRYPTO_FLAG_LEAP)) exten = crypto_args(peer, CRYPTO_TAI, NULL); break; } /* * Build the extension fields as directed. A response to * a request is always sent, even if an error. If an * error occurs when sending a request, the crypto * machinery broke or was misconfigured. In that case * light the crypto bit to suppress further requests. */ if (peer->cmmd != NULL) { peer->cmmd->associd = htonl(peer->associd); sendlen += crypto_xmit(&xpkt, &peer->srcadr, sendlen, peer->cmmd, 0); free(peer->cmmd); peer->cmmd = NULL; } if (exten != NULL) { int ltemp = 0; if (exten->opcode != 0) { ltemp = crypto_xmit(&xpkt, &peer->srcadr, sendlen, exten, 0); if (ltemp == 0) { peer->flash |= TEST9; /* crypto error */ free(exten); return; } } sendlen += ltemp; free(exten); } /* * If extension fields are present, we must use a * private cookie value of zero. Don't send if the * crypto bit is set and no extension field is present, * but in that case give back the key. Most intricate. */ if (sendlen > LEN_PKT_NOMAC) { session_key(&peer->dstadr->sin, &peer->srcadr, xkeyid, 0, 2); } else if (peer->flash & TEST9) { authtrust(xkeyid, 0); return; } } #endif /* OPENSSL */ /* * Stash the transmit timestamp corrected for the encryption * delay. If autokey, give back the key, as we use keys only * once. Check for errors such as missing keys, buffer overflow, * etc. */ xkeyid = peer->keyid; get_systime(&peer->xmt); L_ADD(&peer->xmt, &sys_authdelay); HTONL_FP(&peer->xmt, &xpkt.xmt); authlen = authencrypt(xkeyid, (u_int32 *)&xpkt, sendlen); if (authlen == 0) { msyslog(LOG_INFO, "transmit: %s key %u not found", stoa(&peer->srcadr), xkeyid); peer->flash |= TEST9; /* no key found */ return; } sendlen += authlen; #ifdef OPENSSL if (xkeyid > NTP_MAXKEY) authtrust(xkeyid, 0); #endif /* OPENSSL */ get_systime(&xmt_tx); if (sendlen > sizeof(xpkt)) { msyslog(LOG_ERR, "buffer overflow %u", sendlen); exit (-1); } sendpkt(&peer->srcadr, peer->dstadr, sys_ttl[peer->ttl], &xpkt, sendlen); /* * Calculate the encryption delay. Keep the minimum over * the latest two samples. */ L_SUB(&xmt_tx, &peer->xmt); L_ADD(&xmt_tx, &sys_authdelay); sys_authdly[1] = sys_authdly[0]; sys_authdly[0] = xmt_tx.l_uf; if (sys_authdly[0] < sys_authdly[1]) sys_authdelay.l_uf = sys_authdly[0]; else sys_authdelay.l_uf = sys_authdly[1]; peer->sent++; #ifdef OPENSSL #ifdef DEBUG if (debug) printf( "transmit: at %ld %s->%s mode %d keyid %08x len %d mac %d index %d\n", current_time, peer->dstadr ? ntoa(&peer->dstadr->sin) : "-", ntoa(&peer->srcadr), peer->hmode, xkeyid, sendlen - authlen, authlen, peer->keynumber); #endif #else #ifdef DEBUG if (debug) printf( "transmit: at %ld %s->%s mode %d keyid %08x len %d mac %d\n", current_time, peer->dstadr ? ntoa(&peer->dstadr->sin) : "-", ntoa(&peer->srcadr), peer->hmode, xkeyid, sendlen - authlen, authlen); #endif #endif /* OPENSSL */ } /* * fast_xmit - Send packet for nonpersistent association. Note that * neither the source or destination can be a broadcast address. */ static void fast_xmit( struct recvbuf *rbufp, /* receive packet pointer */ int xmode, /* transmit mode */ keyid_t xkeyid, /* transmit key ID */ int mask /* restrict mask */ ) { struct pkt xpkt; /* transmit packet structure */ struct pkt *rpkt; /* receive packet structure */ l_fp xmt_ts; /* timestamp */ l_fp xmt_tx; /* timestamp after authent */ int sendlen, authlen; #ifdef OPENSSL u_int32 temp32; #endif /* * Initialize transmit packet header fields from the receive * buffer provided. We leave some fields intact as received. If * the gazinta was from a multicast address, the gazoutta must * go out another way. * * The root delay field is special. If the system stratum is * less than the orphan stratum, send the real root delay. * Otherwise, if there is no system peer, send the orphan delay. * Otherwise, we must be an orphan parent, so send zero. */ rpkt = &rbufp->recv_pkt; if (rbufp->dstadr->flags & INT_MCASTOPEN) rbufp->dstadr = findinterface(&rbufp->recv_srcadr); /* * This is deliciously complicated. There are four cases. * * case leap stratum refid delay dispersion * * KoD 11 16 KISS system system * normal system system system system system * orphan child 00 orphan system orphan system * orphan parent 00 orphan loopbk 0 0 */ /* * This is a kiss-of-death (KoD) packet. Show leap * unsynchronized, stratum zero, reference ID the four-character * kiss code and system root delay. Note the rate limit on these * packets. Once a second initialize a bucket counter. Every * packet sent decrements the counter until reaching zero. If * the counter is zero, drop the kiss. */ if (mask & RES_LIMITED) { sys_limitrejected++; if (sys_kod == 0 || !(mask & RES_DEMOBILIZE)) return; sys_kod--; xpkt.li_vn_mode = PKT_LI_VN_MODE(LEAP_NOTINSYNC, PKT_VERSION(rpkt->li_vn_mode), xmode); xpkt.stratum = STRATUM_UNSPEC; memcpy(&xpkt.refid, "RATE", 4); xpkt.rootdelay = HTONS_FP(DTOFP(sys_rootdelay)); xpkt.rootdispersion = HTONS_FP(DTOUFP(sys_rootdispersion)); /* * This is a normal packet. Use the system variables. */ } else if (sys_stratum < sys_orphan) { xpkt.li_vn_mode = PKT_LI_VN_MODE(sys_leap, PKT_VERSION(rpkt->li_vn_mode), xmode); xpkt.stratum = STRATUM_TO_PKT(sys_stratum); xpkt.refid = sys_refid; xpkt.rootdelay = HTONS_FP(DTOFP(sys_rootdelay)); xpkt.rootdispersion = HTONS_FP(DTOUFP(sys_rootdispersion)); /* * This is a orphan child packet. The host is synchronized to an * orphan parent. Show leap synchronized, orphan stratum, system * reference ID and orphan root delay. */ } else if (sys_peer != NULL) { xpkt.li_vn_mode = PKT_LI_VN_MODE(LEAP_NOWARNING, PKT_VERSION(rpkt->li_vn_mode), xmode); xpkt.stratum = STRATUM_TO_PKT(sys_orphan); xpkt.refid = sys_refid; xpkt.rootdelay = HTONS_FP(DTOFP(sys_orphandelay)); xpkt.rootdispersion = HTONS_FP(DTOUFP(sys_rootdispersion)); /* * This is an orphan parent. Show leap synchronized, orphan * stratum, loopack reference ID and zero root delay. */ } else { xpkt.li_vn_mode = PKT_LI_VN_MODE(LEAP_NOWARNING, PKT_VERSION(rpkt->li_vn_mode), xmode); xpkt.stratum = STRATUM_TO_PKT(sys_orphan); xpkt.refid = htonl(LOOPBACKADR); xpkt.rootdelay = HTONS_FP(DTOFP(0)); xpkt.rootdispersion = HTONS_FP(DTOFP(0)); } xpkt.ppoll = rpkt->ppoll; xpkt.precision = sys_precision; xpkt.rootdispersion = HTONS_FP(DTOUFP(sys_rootdispersion)); HTONL_FP(&sys_reftime, &xpkt.reftime); xpkt.org = rpkt->xmt; HTONL_FP(&rbufp->recv_time, &xpkt.rec); /* * If the received packet contains a MAC, the transmitted packet * is authenticated and contains a MAC. If not, the transmitted * packet is not authenticated. */ sendlen = LEN_PKT_NOMAC; if (rbufp->recv_length == sendlen) { get_systime(&xmt_ts); HTONL_FP(&xmt_ts, &xpkt.xmt); sendpkt(&rbufp->recv_srcadr, rbufp->dstadr, 0, &xpkt, sendlen); #ifdef DEBUG if (debug) printf("transmit: at %ld %s->%s mode %d\n", current_time, stoa(&rbufp->dstadr->sin), stoa(&rbufp->recv_srcadr), xmode); #endif return; } /* * The received packet contains a MAC, so the transmitted packet * must be authenticated. For symmetric key cryptography, use * the predefined and trusted symmetric keys to generate the * cryptosum. For autokey cryptography, use the server private * value to generate the cookie, which is unique for every * source-destination-key ID combination. */ #ifdef OPENSSL if (xkeyid > NTP_MAXKEY) { keyid_t cookie; /* * The only way to get here is a reply to a legitimate * client request message, so the mode must be * MODE_SERVER. If an extension field is present, there * can be only one and that must be a command. Do what * needs, but with private value of zero so the poor * jerk can decode it. If no extension field is present, * use the cookie to generate the session key. */ cookie = session_key(&rbufp->recv_srcadr, &rbufp->dstadr->sin, 0, sys_private, 0); if (rbufp->recv_length >= (int)(sendlen + MAX_MAC_LEN + 2 * sizeof(u_int32))) { session_key(&rbufp->dstadr->sin, &rbufp->recv_srcadr, xkeyid, 0, 2); temp32 = CRYPTO_RESP; rpkt->exten[0] |= htonl(temp32); sendlen += crypto_xmit(&xpkt, &rbufp->recv_srcadr, sendlen, (struct exten *)rpkt->exten, cookie); } else { session_key(&rbufp->dstadr->sin, &rbufp->recv_srcadr, xkeyid, cookie, 2); } } #endif /* OPENSSL */ get_systime(&xmt_ts); L_ADD(&xmt_ts, &sys_authdelay); HTONL_FP(&xmt_ts, &xpkt.xmt); authlen = authencrypt(xkeyid, (u_int32 *)&xpkt, sendlen); sendlen += authlen; #ifdef OPENSSL if (xkeyid > NTP_MAXKEY) authtrust(xkeyid, 0); #endif /* OPENSSL */ get_systime(&xmt_tx); if (sendlen > sizeof(xpkt)) { msyslog(LOG_ERR, "buffer overflow %u", sendlen); exit (-1); } sendpkt(&rbufp->recv_srcadr, rbufp->dstadr, 0, &xpkt, sendlen); /* * Calculate the encryption delay. Keep the minimum over the * latest two samples. */ L_SUB(&xmt_tx, &xmt_ts); L_ADD(&xmt_tx, &sys_authdelay); sys_authdly[1] = sys_authdly[0]; sys_authdly[0] = xmt_tx.l_uf; if (sys_authdly[0] < sys_authdly[1]) sys_authdelay.l_uf = sys_authdly[0]; else sys_authdelay.l_uf = sys_authdly[1]; #ifdef DEBUG if (debug) printf( "transmit: at %ld %s->%s mode %d keyid %08x len %d mac %d\n", current_time, ntoa(&rbufp->dstadr->sin), ntoa(&rbufp->recv_srcadr), xmode, xkeyid, sendlen - authlen, authlen); #endif } #ifdef OPENSSL /* * key_expire - purge the key list */ void key_expire( struct peer *peer /* peer structure pointer */ ) { int i; if (peer->keylist != NULL) { for (i = 0; i <= peer->keynumber; i++) authtrust(peer->keylist[i], 0); free(peer->keylist); peer->keylist = NULL; } value_free(&peer->sndval); peer->keynumber = 0; #ifdef DEBUG if (debug) printf("key_expire: at %lu\n", current_time); #endif } #endif /* OPENSSL */ /* * Determine if the peer is unfit for synchronization * * A peer is unfit for synchronization if * > TEST10 bad leap or stratum below floor or at or above ceiling * > TEST11 root distance exceeded * > TEST12 a direct or indirect synchronization loop would form * > TEST13 unreachable or noselect */ int /* FALSE if fit, TRUE if unfit */ peer_unfit( struct peer *peer /* peer structure pointer */ ) { int rval = 0; /* * A stratum error occurs if (1) the server has never been * synchronized, (2) the server stratum is below the floor or * greater than or equal to the ceiling, (3) the system stratum * is below the orphan stratum and the server stratum is greater * than or equal to the orphan stratum. */ if (peer->leap == LEAP_NOTINSYNC || peer->stratum < sys_floor || peer->stratum >= sys_ceiling || (sys_stratum < sys_orphan && peer->stratum >= sys_orphan)) rval |= TEST10; /* stratum out of bounds */ /* * A distance error occurs if the root distance is greater than * or equal to the distance threshold plus the increment due to * one poll interval. */ if (root_distance(peer) >= sys_maxdist + clock_phi * ULOGTOD(sys_poll)) rval |= TEST11; /* distance exceeded */ /* * A loop error occurs if the remote peer is synchronized to the * local peer of if the remote peer is synchronized to the same * server as the local peer, but only if the remote peer is not * the orphan parent. */ if (peer->stratum > 1 && peer->refid != htonl(LOOPBACKADR) && ((!peer->dstadr || peer->refid == peer->dstadr->addr_refid) || peer->refid == sys_refid)) rval |= TEST12; /* synch loop */ /* * An unreachable error occurs if the server is unreachable or * the noselect bit is set. */ if (!peer->reach || peer->flags & FLAG_NOSELECT) rval |= TEST13; /* unreachable */ peer->flash &= ~PEER_TEST_MASK; peer->flash |= rval; return (rval); } /* * Find the precision of this particular machine */ #define MINSTEP 100e-9 /* minimum clock increment (s) */ #define MAXSTEP 20e-3 /* maximum clock increment (s) */ #define MINLOOPS 5 /* minimum number of step samples */ /* * This routine calculates the system precision, defined as the minimum * of a sequence of differences between successive readings of the * system clock. However, if the system clock can be read more than once * during a tick interval, the difference can be zero or one LSB unit, * where the LSB corresponds to one nanosecond or one microsecond. * Conceivably, if some other process preempts this one and reads the * clock, the difference can be more than one LSB unit. * * For hardware clock frequencies of 10 MHz or less, we assume the * logical clock advances only at the hardware clock tick. For higher * frequencies, we assume the logical clock can advance no more than 100 * nanoseconds between ticks. */ int default_get_precision(void) { l_fp val; /* current seconds fraction */ l_fp last; /* last seconds fraction */ l_fp diff; /* difference */ double tick; /* computed tick value */ double dtemp; /* scratch */ int i; /* log2 precision */ /* * Loop to find tick value in nanoseconds. Toss out outlyer * values less than the minimun tick value. In wacky cases, use * the default maximum value. */ get_systime(&last); tick = MAXSTEP; for (i = 0; i < MINLOOPS;) { get_systime(&val); diff = val; L_SUB(&diff, &last); last = val; LFPTOD(&diff, dtemp); if (dtemp < MINSTEP) continue; i++; if (dtemp < tick) tick = dtemp; } /* * Find the nearest power of two. */ NLOG(NLOG_SYSEVENT) msyslog(LOG_INFO, "precision = %.3f usec", tick * 1e6); for (i = 0; tick <= 1; i++) tick *= 2; if (tick - 1. > 1. - tick / 2) i--; return (-i); } /* * kod_proto - called once per second to limit kiss-of-death packets */ void kod_proto(void) { sys_kod = sys_kod_rate; } /* * init_proto - initialize the protocol module's data */ void init_proto(void) { l_fp dummy; int i; /* * Fill in the sys_* stuff. Default is don't listen to * broadcasting, authenticate. */ sys_leap = LEAP_NOTINSYNC; sys_stratum = STRATUM_UNSPEC; memcpy(&sys_refid, "INIT", 4); sys_precision = (s_char)default_get_precision(); sys_jitter = LOGTOD(sys_precision); sys_rootdelay = 0; sys_orphandelay = (double)(ntp_random() & 0xffff) / 65536. * sys_maxdist; sys_rootdispersion = 0; L_CLR(&sys_reftime); sys_peer = NULL; sys_survivors = 0; get_systime(&dummy); sys_manycastserver = 0; sys_bclient = 0; sys_bdelay = DEFBROADDELAY; sys_calldelay = BURST_DELAY; sys_authenticate = 1; L_CLR(&sys_authdelay); sys_authdly[0] = sys_authdly[1] = 0; sys_stattime = 0; proto_clr_stats(); for (i = 0; i < MAX_TTL; i++) { sys_ttl[i] = (u_char)((i * 256) / MAX_TTL); sys_ttlmax = i; } #ifdef OPENSSL sys_automax = 1 << NTP_AUTOMAX; #endif /* OPENSSL */ /* * Default these to enable */ ntp_enable = 1; #ifndef KERNEL_FLL_BUG kern_enable = 1; #endif pps_enable = 0; stats_control = 1; } /* * proto_config - configure the protocol module */ void proto_config( int item, u_long value, double dvalue, struct sockaddr_storage* svalue ) { /* * Figure out what he wants to change, then do it */ switch (item) { /* * Turn on/off kernel discipline. */ case PROTO_KERNEL: kern_enable = (int)value; break; /* * Turn on/off clock discipline. */ case PROTO_NTP: ntp_enable = (int)value; break; /* * Turn on/off monitoring. */ case PROTO_MONITOR: if (value) mon_start(MON_ON); else mon_stop(MON_ON); break; /* * Turn on/off statistics. */ case PROTO_FILEGEN: stats_control = (int)value; break; /* * Turn on/off enable broadcasts. */ case PROTO_BROADCLIENT: sys_bclient = (int)value; if (sys_bclient == 0) io_unsetbclient(); else io_setbclient(); break; /* * Turn on/off PPS discipline. */ case PROTO_PPS: pps_enable = (int)value; break; /* * Add muliticast group address. */ case PROTO_MULTICAST_ADD: if (svalue) io_multicast_add(*svalue); sys_bclient = 1; break; /* * Delete multicast group address. */ case PROTO_MULTICAST_DEL: if (svalue) io_multicast_del(*svalue); break; /* * Set default broadcast delay. */ case PROTO_BROADDELAY: sys_bdelay = dvalue; break; /* * Set modem call delay. */ case PROTO_CALLDELAY: sys_calldelay = (int)value; break; /* * Turn on/off authentication to mobilize ephemeral * associations. */ case PROTO_AUTHENTICATE: sys_authenticate = (int)value; break; /* * Set minimum number of survivors. */ case PROTO_MINCLOCK: sys_minclock = (int)dvalue; break; /* * Set maximum number of preemptable associations. */ case PROTO_MAXCLOCK: sys_maxclock = (int)dvalue; break; /* * Set minimum number of survivors. */ case PROTO_MINSANE: sys_minsane = (int)dvalue; break; /* * Set stratum floor. */ case PROTO_FLOOR: sys_floor = (int)dvalue; break; /* * Set stratum ceiling. */ case PROTO_CEILING: sys_ceiling = (int)dvalue; break; /* * Set orphan stratum. */ case PROTO_ORPHAN: sys_orphan = (int)dvalue; break; /* * Set cohort switch. */ case PROTO_COHORT: sys_cohort = (int)dvalue; break; /* * Set minimum dispersion increment. */ case PROTO_MINDISP: sys_mindisp = dvalue; break; /* * Set maximum distance (select threshold). */ case PROTO_MAXDIST: sys_maxdist = dvalue; break; /* * Set anticlockhop threshold. */ case PROTO_MAXHOP: sys_maxhop = (int)dvalue; break; /* * Set adjtime() resolution (s). */ case PROTO_ADJ: sys_tick = dvalue; break; /* * Set manycast beacon interval. */ case PROTO_BEACON: sys_beacon = (int)dvalue; break; #ifdef REFCLOCK /* * Turn on/off refclock calibrate */ case PROTO_CAL: cal_enable = (int)value; break; #endif /* REFCLOCK */ default: /* * Log this error. */ msyslog(LOG_INFO, "proto_config: illegal item %d, value %ld", item, value); } } /* * proto_clr_stats - clear protocol stat counters */ void proto_clr_stats(void) { sys_stattime = current_time; sys_received = 0; sys_processed = 0; sys_newversionpkt = 0; sys_oldversionpkt = 0; sys_unknownversion = 0; sys_restricted = 0; sys_badlength = 0; sys_badauth = 0; sys_limitrejected = 0; } Index: head/contrib/ntp/util/ntp-keygen.c =================================================================== --- head/contrib/ntp/util/ntp-keygen.c (revision 276070) +++ head/contrib/ntp/util/ntp-keygen.c (revision 276071) @@ -1,1890 +1,1890 @@ /* * Program to generate cryptographic keys for NTP clients and servers * * This program generates files "ntpkey__.", * where is the file type, is the generating host and * is the NTP seconds in decimal format. The NTP programs * expect generic names such as "ntpkey__whimsy.udel.edu" with the * association maintained by soft links. * * Files are prefixed with a header giving the name and date of creation * followed by a type-specific descriptive label and PEM-encoded data * string compatible with programs of the OpenSSL library. * * Note that private keys can be password encrypted as per OpenSSL * conventions. * * The file types include * * ntpkey_MD5key_. * MD5 (128-bit) keys used to compute message digests in symmetric * key cryptography * * ntpkey_RSAkey_. * ntpkey_host_ (RSA) link * RSA private/public host key pair used for public key signatures * and data encryption * * ntpkey_DSAkey_. * ntpkey_sign_ (RSA or DSA) link * DSA private/public sign key pair used for public key signatures, * but not data encryption * * ntpkey_IFFpar_. * ntpkey_iff_ (IFF server/client) link * ntpkey_iffkey_ (IFF client) link * Schnorr (IFF) server/client identity parameters * * ntpkey_IFFkey_. * Schnorr (IFF) client identity parameters * * ntpkey_GQpar_., * ntpkey_gq_ (GQ) link * Guillou-Quisquater (GQ) identity parameters * * ntpkey_MVpar_., * Mu-Varadharajan (MV) server identity parameters * * ntpkey_MVkeyX_., * ntpkey_mv_ (MV server) link * ntpkey_mvkey_ (MV client) link * Mu-Varadharajan (MV) client identity parameters * * ntpkey_XXXcert_. * ntpkey_cert_ (RSA or DSA) link * X509v3 certificate using RSA or DSA public keys and signatures. * XXX is a code identifying the message digest and signature * encryption algorithm * * Available digest/signature schemes * * RSA: RSA-MD2, RSA-MD5, RSA-SHA, RSA-SHA1, RSA-MDC2, EVP-RIPEMD160 * DSA: DSA-SHA, DSA-SHA1 * * Note: Once in a while because of some statistical fluke this program * fails to generate and verify some cryptographic data, as indicated by * exit status -1. In this case simply run the program again. If the * program does complete with return code 0, the data are correct as * verified. * * These cryptographic routines are characterized by the prime modulus * size in bits. The default value of 512 bits is a compromise between * cryptographic strength and computing time and is ordinarily * considered adequate for this application. The routines have been * tested with sizes of 256, 512, 1024 and 2048 bits. Not all message * digest and signature encryption schemes work with sizes less than 512 * bits. The computing time for sizes greater than 2048 bits is * prohibitive on all but the fastest processors. An UltraSPARC Blade * 1000 took something over nine minutes to generate and verify the * values with size 2048. An old SPARC IPC would take a week. * * The OpenSSL library used by this program expects a random seed file. * As described in the OpenSSL documentation, the file name defaults to * first the RANDFILE environment variable in the user's home directory * and then .rnd in the user's home directory. */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #if HAVE_SYS_TYPES_H # include #endif #include "ntp_types.h" #include "ntp_random.h" #include "l_stdlib.h" #include "ntp-keygen-opts.h" #ifdef SYS_WINNT extern int ntp_getopt P((int, char **, const char *)); #define getopt ntp_getopt #define optarg ntp_optarg #endif #ifdef OPENSSL #include "openssl/bn.h" #include "openssl/evp.h" #include "openssl/err.h" #include "openssl/rand.h" #include "openssl/pem.h" #include "openssl/x509v3.h" #include #endif /* OPENSSL */ /* * Cryptodefines */ #define MD5KEYS 16 /* number of MD5 keys generated */ #define JAN_1970 ULONG_CONST(2208988800) /* NTP seconds */ #define YEAR ((long)60*60*24*365) /* one year in seconds */ #define MAXFILENAME 256 /* max file name length */ #define MAXHOSTNAME 256 /* max host name length */ #ifdef OPENSSL #define PLEN 512 /* default prime modulus size (bits) */ /* * Strings used in X509v3 extension fields */ #define KEY_USAGE "digitalSignature,keyCertSign" #define BASIC_CONSTRAINTS "critical,CA:TRUE" #define EXT_KEY_PRIVATE "private" #define EXT_KEY_TRUST "trustRoot" #endif /* OPENSSL */ /* * Prototypes */ FILE *fheader P((const char *, const char *)); void fslink P((const char *, const char *)); int gen_md5 P((char *)); #ifdef OPENSSL EVP_PKEY *gen_rsa P((char *)); EVP_PKEY *gen_dsa P((char *)); EVP_PKEY *gen_iff P((char *)); EVP_PKEY *gen_gqpar P((char *)); EVP_PKEY *gen_gqkey P((char *, EVP_PKEY *)); EVP_PKEY *gen_mv P((char *)); int x509 P((EVP_PKEY *, const EVP_MD *, char *, char *)); void cb P((int, int, void *)); EVP_PKEY *genkey P((char *, char *)); u_long asn2ntp P((ASN1_TIME *)); #endif /* OPENSSL */ /* * Program variables */ extern char *optarg; /* command line argument */ int debug = 0; /* debug, not de bug */ int rval; /* return status */ #ifdef OPENSSL u_int modulus = PLEN; /* prime modulus size (bits) */ #endif int nkeys = 0; /* MV keys */ time_t epoch; /* Unix epoch (seconds) since 1970 */ char *hostname; /* host name (subject name) */ char *trustname; /* trusted host name (issuer name) */ char filename[MAXFILENAME + 1]; /* file name */ char *passwd1 = NULL; /* input private key password */ char *passwd2 = NULL; /* output private key password */ #ifdef OPENSSL long d0, d1, d2, d3; /* callback counters */ #endif /* OPENSSL */ #ifdef SYS_WINNT BOOL init_randfile(); /* * Don't try to follow symbolic links */ int readlink(char * link, char * file, int len) { return (-1); } /* * Don't try to create a symbolic link for now. * Just move the file to the name you need. */ int symlink(char *filename, char *linkname) { DeleteFile(linkname); MoveFile(filename, linkname); return 0; } void InitWin32Sockets() { WORD wVersionRequested; WSADATA wsaData; wVersionRequested = MAKEWORD(2,0); if (WSAStartup(wVersionRequested, &wsaData)) { fprintf(stderr, "No useable winsock.dll"); exit(1); } } #endif /* SYS_WINNT */ /* * Main program */ int main( int argc, /* command line options */ char **argv ) { struct timeval tv; /* initialization vector */ int md5key = 0; /* generate MD5 keys */ #ifdef OPENSSL X509 *cert = NULL; /* X509 certificate */ EVP_PKEY *pkey_host = NULL; /* host key */ EVP_PKEY *pkey_sign = NULL; /* sign key */ EVP_PKEY *pkey_iff = NULL; /* IFF parameters */ EVP_PKEY *pkey_gq = NULL; /* GQ parameters */ EVP_PKEY *pkey_mv = NULL; /* MV parameters */ int hostkey = 0; /* generate RSA keys */ int iffkey = 0; /* generate IFF parameters */ int gqpar = 0; /* generate GQ parameters */ int gqkey = 0; /* update GQ keys */ int mvpar = 0; /* generate MV parameters */ int mvkey = 0; /* update MV keys */ char *sign = NULL; /* sign key */ EVP_PKEY *pkey = NULL; /* temp key */ const EVP_MD *ectx; /* EVP digest */ char pathbuf[MAXFILENAME + 1]; const char *scheme = NULL; /* digest/signature scheme */ char *exten = NULL; /* private extension */ char *grpkey = NULL; /* identity extension */ int nid; /* X509 digest/signature scheme */ FILE *fstr = NULL; /* file handle */ u_int temp; #define iffsw HAVE_OPT(ID_KEY) #endif /* OPENSSL */ char hostbuf[MAXHOSTNAME + 1]; #ifdef SYS_WINNT /* Initialize before OpenSSL checks */ InitWin32Sockets(); if(!init_randfile()) fprintf(stderr, "Unable to initialize .rnd file\n"); #endif #ifdef OPENSSL /* * OpenSSL version numbers: MNNFFPPS: major minor fix patch status * We match major, minor, fix and status (not patch) */ if ((SSLeay() ^ OPENSSL_VERSION_NUMBER) & ~0xff0L) { fprintf(stderr, "OpenSSL version mismatch. Built against %lx, you have %lx\n", OPENSSL_VERSION_NUMBER, SSLeay()); return (-1); } else { fprintf(stderr, "Using OpenSSL version %lx\n", SSLeay()); } #endif /* OPENSSL */ /* * Process options, initialize host name and timestamp. */ gethostname(hostbuf, MAXHOSTNAME); hostname = hostbuf; #ifdef OPENSSL trustname = hostbuf; passwd1 = hostbuf; #endif #ifndef SYS_WINNT gettimeofday(&tv, 0); #else gettimeofday(&tv); #endif epoch = tv.tv_sec; rval = 0; { int optct = optionProcess(&ntp_keygenOptions, argc, argv); argc -= optct; argv += optct; } #ifdef OPENSSL if (HAVE_OPT( CERTIFICATE )) scheme = OPT_ARG( CERTIFICATE ); #endif debug = DESC(DEBUG_LEVEL).optOccCt; #ifdef OPENSSL if (HAVE_OPT( GQ_PARAMS )) gqpar++; if (HAVE_OPT( GQ_KEYS )) gqkey++; if (HAVE_OPT( HOST_KEY )) hostkey++; if (HAVE_OPT( IFFKEY )) iffkey++; if (HAVE_OPT( ISSUER_NAME )) trustname = OPT_ARG( ISSUER_NAME ); #endif if (HAVE_OPT( MD5KEY )) md5key++; #ifdef OPENSSL if (HAVE_OPT( MODULUS )) modulus = OPT_VALUE_MODULUS; if (HAVE_OPT( PVT_CERT )) exten = EXT_KEY_PRIVATE; if (HAVE_OPT( PVT_PASSWD )) passwd2 = OPT_ARG( PVT_PASSWD ); if (HAVE_OPT( GET_PVT_PASSWD )) passwd1 = OPT_ARG( GET_PVT_PASSWD ); if (HAVE_OPT( SIGN_KEY )) sign = OPT_ARG( SIGN_KEY ); if (HAVE_OPT( SUBJECT_NAME )) hostname = OPT_ARG( SUBJECT_NAME ); if (HAVE_OPT( TRUSTED_CERT )) exten = EXT_KEY_TRUST; if (HAVE_OPT( MV_PARAMS )) { mvpar++; nkeys = OPT_VALUE_MV_PARAMS; } if (HAVE_OPT( MV_KEYS )) { mvkey++; nkeys = OPT_VALUE_MV_KEYS; } #endif if (passwd1 != NULL && passwd2 == NULL) passwd2 = passwd1; #ifdef OPENSSL /* * Seed random number generator and grow weeds. */ ERR_load_crypto_strings(); OpenSSL_add_all_algorithms(); if (RAND_file_name(pathbuf, MAXFILENAME) == NULL) { fprintf(stderr, "RAND_file_name %s\n", ERR_error_string(ERR_get_error(), NULL)); return (-1); } temp = RAND_load_file(pathbuf, -1); if (temp == 0) { fprintf(stderr, "RAND_load_file %s not found or empty\n", pathbuf); return (-1); } fprintf(stderr, "Random seed file %s %u bytes\n", pathbuf, temp); RAND_add(&epoch, sizeof(epoch), 4.0); #endif /* * Generate new parameters and keys as requested. These replace * any values already generated. */ if (md5key) gen_md5("MD5"); #ifdef OPENSSL if (hostkey) pkey_host = genkey("RSA", "host"); if (sign != NULL) pkey_sign = genkey(sign, "sign"); if (iffkey) pkey_iff = gen_iff("iff"); if (gqpar) pkey_gq = gen_gqpar("gq"); if (mvpar) pkey_mv = gen_mv("mv"); /* * If there is no new host key, look for an existing one. If not * found, create it. */ while (pkey_host == NULL && rval == 0 && !HAVE_OPT(ID_KEY)) { sprintf(filename, "ntpkey_host_%s", hostname); if ((fstr = fopen(filename, "r")) != NULL) { pkey_host = PEM_read_PrivateKey(fstr, NULL, NULL, passwd1); fclose(fstr); readlink(filename, filename, sizeof(filename)); if (pkey_host == NULL) { fprintf(stderr, "Host key\n%s\n", ERR_error_string(ERR_get_error(), NULL)); rval = -1; } else { fprintf(stderr, "Using host key %s\n", filename); } break; } else if ((pkey_host = genkey("RSA", "host")) == NULL) { rval = -1; break; } } /* * If there is no new sign key, look for an existing one. If not * found, use the host key instead. */ pkey = pkey_sign; while (pkey_sign == NULL && rval == 0 && !HAVE_OPT(ID_KEY)) { sprintf(filename, "ntpkey_sign_%s", hostname); if ((fstr = fopen(filename, "r")) != NULL) { pkey_sign = PEM_read_PrivateKey(fstr, NULL, NULL, passwd1); fclose(fstr); readlink(filename, filename, sizeof(filename)); if (pkey_sign == NULL) { fprintf(stderr, "Sign key\n%s\n", ERR_error_string(ERR_get_error(), NULL)); rval = -1; } else { fprintf(stderr, "Using sign key %s\n", filename); } break; } else { pkey = pkey_host; fprintf(stderr, "Using host key as sign key\n"); break; } } /* * If there is no new IFF file, look for an existing one. */ if (pkey_iff == NULL && rval == 0) { sprintf(filename, "ntpkey_iff_%s", hostname); if ((fstr = fopen(filename, "r")) != NULL) { pkey_iff = PEM_read_PrivateKey(fstr, NULL, NULL, passwd1); fclose(fstr); readlink(filename, filename, sizeof(filename)); if (pkey_iff == NULL) { fprintf(stderr, "IFF parameters\n%s\n", ERR_error_string(ERR_get_error(), NULL)); rval = -1; } else { fprintf(stderr, "Using IFF parameters %s\n", filename); } } } /* * If there is no new GQ file, look for an existing one. */ if (pkey_gq == NULL && rval == 0 && !HAVE_OPT(ID_KEY)) { sprintf(filename, "ntpkey_gq_%s", hostname); if ((fstr = fopen(filename, "r")) != NULL) { pkey_gq = PEM_read_PrivateKey(fstr, NULL, NULL, passwd1); fclose(fstr); readlink(filename, filename, sizeof(filename)); if (pkey_gq == NULL) { fprintf(stderr, "GQ parameters\n%s\n", ERR_error_string(ERR_get_error(), NULL)); rval = -1; } else { fprintf(stderr, "Using GQ parameters %s\n", filename); } } } /* * If there is a GQ parameter file, create GQ private/public * keys and extract the public key for the certificate. */ if (pkey_gq != NULL && rval == 0) { gen_gqkey("gq", pkey_gq); grpkey = BN_bn2hex(pkey_gq->pkey.rsa->q); } /* * Generate a X509v3 certificate. */ while (scheme == NULL && rval == 0 && !HAVE_OPT(ID_KEY)) { sprintf(filename, "ntpkey_cert_%s", hostname); if ((fstr = fopen(filename, "r")) != NULL) { cert = PEM_read_X509(fstr, NULL, NULL, NULL); fclose(fstr); readlink(filename, filename, sizeof(filename)); if (cert == NULL) { fprintf(stderr, "Cert \n%s\n", ERR_error_string(ERR_get_error(), NULL)); rval = -1; } else { nid = OBJ_obj2nid( cert->cert_info->signature->algorithm); scheme = OBJ_nid2sn(nid); fprintf(stderr, "Using scheme %s from %s\n", scheme, filename); break; } } scheme = "RSA-MD5"; } if (pkey != NULL && rval == 0 && !HAVE_OPT(ID_KEY)) { ectx = EVP_get_digestbyname(scheme); if (ectx == NULL) { fprintf(stderr, "Invalid digest/signature combination %s\n", scheme); rval = -1; } else { x509(pkey, ectx, grpkey, exten); } } /* * Write the IFF client parameters and keys as a DSA private key * encoded in PEM. Note the private key is obscured. */ if (pkey_iff != NULL && rval == 0 && HAVE_OPT(ID_KEY)) { DSA *dsa; char *sptr; char *tld; sptr = strrchr(filename, '.'); tld = malloc(strlen(sptr)); /* we have an extra byte ... */ strcpy(tld, 1+sptr); /* ... see? */ sprintf(filename, "ntpkey_IFFkey_%s.%s", trustname, tld); free(tld); fprintf(stderr, "Writing new IFF key %s\n", filename); fprintf(stdout, "# %s\n# %s", filename, ctime(&epoch)); dsa = pkey_iff->pkey.dsa; BN_copy(dsa->priv_key, BN_value_one()); pkey = EVP_PKEY_new(); EVP_PKEY_assign_DSA(pkey, dsa); PEM_write_PrivateKey(stdout, pkey, passwd2 ? EVP_des_cbc() : NULL, NULL, 0, NULL, passwd2); fclose(stdout); if (debug) DSA_print_fp(stdout, dsa, 0); } /* * Return the marbles. */ if (grpkey != NULL) OPENSSL_free(grpkey); if (pkey_host != NULL) EVP_PKEY_free(pkey_host); if (pkey_sign != NULL) EVP_PKEY_free(pkey_sign); if (pkey_iff != NULL) EVP_PKEY_free(pkey_iff); if (pkey_gq != NULL) EVP_PKEY_free(pkey_gq); if (pkey_mv != NULL) EVP_PKEY_free(pkey_mv); #endif /* OPENSSL */ return (rval); } #if 0 /* * Generate random MD5 key with password. */ int gen_md5( char *id /* file name id */ ) { BIGNUM *key; BIGNUM *keyid; FILE *str; u_char bin[16]; fprintf(stderr, "Generating MD5 keys...\n"); str = fheader("MD5key", hostname); keyid = BN_new(); key = BN_new(); BN_rand(keyid, 16, -1, 0); BN_rand(key, 128, -1, 0); BN_bn2bin(key, bin); PEM_write_fp(str, MD5, NULL, bin); fclose(str); fslink(id, hostname); return (1); } #else /* * Generate semi-random MD5 keys compatible with NTPv3 and NTPv4 */ int gen_md5( char *id /* file name id */ ) { u_char md5key[16]; /* MD5 key */ FILE *str; u_int temp = 0; /* Initialize to prevent warnings during compile */ int i, j; fprintf(stderr, "Generating MD5 keys...\n"); str = fheader("MD5key", hostname); ntp_srandom(epoch); for (i = 1; i <= MD5KEYS; i++) { for (j = 0; j < 16; j++) { while (1) { - temp = ntp_random() & 0xff; + temp = arc4random() & 0xff; if (temp == '#') continue; if (temp > 0x20 && temp < 0x7f) break; } md5key[j] = (u_char)temp; } md5key[15] = '\0'; fprintf(str, "%2d MD5 %16s # MD5 key\n", i, md5key); } fclose(str); fslink(id, hostname); return (1); } #endif /* OPENSSL */ #ifdef OPENSSL /* * Generate RSA public/private key pair */ EVP_PKEY * /* public/private key pair */ gen_rsa( char *id /* file name id */ ) { EVP_PKEY *pkey; /* private key */ RSA *rsa; /* RSA parameters and key pair */ FILE *str; fprintf(stderr, "Generating RSA keys (%d bits)...\n", modulus); - rsa = RSA_generate_key(modulus, 3, cb, "RSA"); + rsa = RSA_generate_key(modulus, 65537, cb, "RSA"); fprintf(stderr, "\n"); if (rsa == NULL) { fprintf(stderr, "RSA generate keys fails\n%s\n", ERR_error_string(ERR_get_error(), NULL)); rval = -1; return (NULL); } /* * For signature encryption it is not necessary that the RSA * parameters be strictly groomed and once in a while the * modulus turns out to be non-prime. Just for grins, we check * the primality. */ if (!RSA_check_key(rsa)) { fprintf(stderr, "Invalid RSA key\n%s\n", ERR_error_string(ERR_get_error(), NULL)); RSA_free(rsa); rval = -1; return (NULL); } /* * Write the RSA parameters and keys as a RSA private key * encoded in PEM. */ str = fheader("RSAkey", hostname); pkey = EVP_PKEY_new(); EVP_PKEY_assign_RSA(pkey, rsa); PEM_write_PrivateKey(str, pkey, passwd2 ? EVP_des_cbc() : NULL, NULL, 0, NULL, passwd2); fclose(str); if (debug) RSA_print_fp(stdout, rsa, 0); fslink(id, hostname); return (pkey); } /* * Generate DSA public/private key pair */ EVP_PKEY * /* public/private key pair */ gen_dsa( char *id /* file name id */ ) { EVP_PKEY *pkey; /* private key */ DSA *dsa; /* DSA parameters */ u_char seed[20]; /* seed for parameters */ FILE *str; /* * Generate DSA parameters. */ fprintf(stderr, "Generating DSA parameters (%d bits)...\n", modulus); RAND_bytes(seed, sizeof(seed)); dsa = DSA_generate_parameters(modulus, seed, sizeof(seed), NULL, NULL, cb, "DSA"); fprintf(stderr, "\n"); if (dsa == NULL) { fprintf(stderr, "DSA generate parameters fails\n%s\n", ERR_error_string(ERR_get_error(), NULL)); rval = -1; return (NULL); } /* * Generate DSA keys. */ fprintf(stderr, "Generating DSA keys (%d bits)...\n", modulus); if (!DSA_generate_key(dsa)) { fprintf(stderr, "DSA generate keys fails\n%s\n", ERR_error_string(ERR_get_error(), NULL)); DSA_free(dsa); rval = -1; return (NULL); } /* * Write the DSA parameters and keys as a DSA private key * encoded in PEM. */ str = fheader("DSAkey", hostname); pkey = EVP_PKEY_new(); EVP_PKEY_assign_DSA(pkey, dsa); PEM_write_PrivateKey(str, pkey, passwd2 ? EVP_des_cbc() : NULL, NULL, 0, NULL, passwd2); fclose(str); if (debug) DSA_print_fp(stdout, dsa, 0); fslink(id, hostname); return (pkey); } /* * Generate Schnorr (IFF) parameters and keys * * The Schnorr (IFF)identity scheme is intended for use when * certificates are generated by some other trusted certificate * authority and the parameters cannot be conveyed in the certificate * itself. For this purpose, new generations of IFF values must be * securely transmitted to all members of the group before use. There * are two kinds of files: server/client files that include private and * public parameters and client files that include only public * parameters. The scheme is self contained and independent of new * generations of host keys, sign keys and certificates. * * The IFF values hide in a DSA cuckoo structure which uses the same * parameters. The values are used by an identity scheme based on DSA * cryptography and described in Stimson p. 285. The p is a 512-bit * prime, g a generator of Zp* and q a 160-bit prime that divides p - 1 * and is a qth root of 1 mod p; that is, g^q = 1 mod p. The TA rolls a * private random group key b (0 < b < q), then computes public * v = g^(q - a). All values except the group key are known to all group * members; the group key is known to the group servers, but not the * group clients. Alice challenges Bob to confirm identity using the * protocol described below. */ EVP_PKEY * /* DSA cuckoo nest */ gen_iff( char *id /* file name id */ ) { EVP_PKEY *pkey; /* private key */ DSA *dsa; /* DSA parameters */ u_char seed[20]; /* seed for parameters */ BN_CTX *ctx; /* BN working space */ BIGNUM *b, *r, *k, *u, *v, *w; /* BN temp */ FILE *str; u_int temp; /* * Generate DSA parameters for use as IFF parameters. */ fprintf(stderr, "Generating IFF parameters (%d bits)...\n", modulus); RAND_bytes(seed, sizeof(seed)); dsa = DSA_generate_parameters(modulus, seed, sizeof(seed), NULL, NULL, cb, "IFF"); fprintf(stderr, "\n"); if (dsa == NULL) { fprintf(stderr, "DSA generate parameters fails\n%s\n", ERR_error_string(ERR_get_error(), NULL)); rval = -1; return (NULL);; } /* * Generate the private and public keys. The DSA parameters and * these keys are distributed to all members of the group. */ fprintf(stderr, "Generating IFF keys (%d bits)...\n", modulus); b = BN_new(); r = BN_new(); k = BN_new(); u = BN_new(); v = BN_new(); w = BN_new(); ctx = BN_CTX_new(); BN_rand(b, BN_num_bits(dsa->q), -1, 0); /* a */ BN_mod(b, b, dsa->q, ctx); BN_sub(v, dsa->q, b); BN_mod_exp(v, dsa->g, v, dsa->p, ctx); /* g^(q - b) mod p */ BN_mod_exp(u, dsa->g, b, dsa->p, ctx); /* g^b mod p */ BN_mod_mul(u, u, v, dsa->p, ctx); temp = BN_is_one(u); fprintf(stderr, "Confirm g^(q - b) g^b = 1 mod p: %s\n", temp == 1 ? "yes" : "no"); if (!temp) { BN_free(b); BN_free(r); BN_free(k); BN_free(u); BN_free(v); BN_free(w); BN_CTX_free(ctx); rval = -1; return (NULL); } dsa->priv_key = BN_dup(b); /* private key */ dsa->pub_key = BN_dup(v); /* public key */ /* * Here is a trial round of the protocol. First, Alice rolls * random r (0 < r < q) and sends it to Bob. She needs only * modulus q. */ BN_rand(r, BN_num_bits(dsa->q), -1, 0); /* r */ BN_mod(r, r, dsa->q, ctx); /* * Bob rolls random k (0 < k < q), computes y = k + b r mod q * and x = g^k mod p, then sends (y, x) to Alice. He needs * moduli p, q and the group key b. */ BN_rand(k, BN_num_bits(dsa->q), -1, 0); /* k, 0 < k < q */ BN_mod(k, k, dsa->q, ctx); BN_mod_mul(v, dsa->priv_key, r, dsa->q, ctx); /* b r mod q */ BN_add(v, v, k); BN_mod(v, v, dsa->q, ctx); /* y = k + b r mod q */ BN_mod_exp(u, dsa->g, k, dsa->p, ctx); /* x = g^k mod p */ /* * Alice computes g^y v^r and verifies the result is equal to x. * She needs modulus p, generator g, and the public key v, as * well as her original r. */ BN_mod_exp(v, dsa->g, v, dsa->p, ctx); /* g^y mod p */ BN_mod_exp(w, dsa->pub_key, r, dsa->p, ctx); /* v^r */ BN_mod_mul(v, w, v, dsa->p, ctx); /* product mod p */ temp = BN_cmp(u, v); fprintf(stderr, "Confirm g^k = g^(k + b r) g^(q - b) r: %s\n", temp == 0 ? "yes" : "no"); BN_free(b); BN_free(r); BN_free(k); BN_free(u); BN_free(v); BN_free(w); BN_CTX_free(ctx); if (temp != 0) { DSA_free(dsa); rval = -1; return (NULL); } /* * Write the IFF server parameters and keys as a DSA private key * encoded in PEM. * * p modulus p * q modulus q * g generator g * priv_key b * public_key v */ str = fheader("IFFpar", trustname); pkey = EVP_PKEY_new(); EVP_PKEY_assign_DSA(pkey, dsa); PEM_write_PrivateKey(str, pkey, passwd2 ? EVP_des_cbc() : NULL, NULL, 0, NULL, passwd2); fclose(str); if (debug) DSA_print_fp(stdout, dsa, 0); fslink(id, trustname); return (pkey); } /* * Generate Guillou-Quisquater (GQ) parameters and keys * * The Guillou-Quisquater (GQ) identity scheme is intended for use when * the parameters, keys and certificates are generated by this program. * The scheme uses a certificate extension field do convey the public * key of a particular group identified by a group key known only to * members of the group. The scheme is self contained and independent of * new generations of host keys and sign keys. * * The GQ parameters hide in a RSA cuckoo structure which uses the same * parameters. The values are used by an identity scheme based on RSA * cryptography and described in Stimson p. 300 (with errors). The 512- * bit public modulus is n = p q, where p and q are secret large primes. * The TA rolls private random group key b as RSA exponent. These values * are known to all group members. * * When rolling new certificates, a member recomputes the private and * public keys. The private key u is a random roll, while the public key * is the inverse obscured by the group key v = (u^-1)^b. These values * replace the private and public keys normally generated by the RSA * scheme. Alice challenges Bob to confirm identity using the protocol * described below. */ EVP_PKEY * /* RSA cuckoo nest */ gen_gqpar( char *id /* file name id */ ) { EVP_PKEY *pkey; /* private key */ RSA *rsa; /* GQ parameters */ BN_CTX *ctx; /* BN working space */ FILE *str; /* * Generate RSA parameters for use as GQ parameters. */ fprintf(stderr, "Generating GQ parameters (%d bits)...\n", modulus); - rsa = RSA_generate_key(modulus, 3, cb, "GQ"); + rsa = RSA_generate_key(modulus, 65537, cb, "GQ"); fprintf(stderr, "\n"); if (rsa == NULL) { fprintf(stderr, "RSA generate keys fails\n%s\n", ERR_error_string(ERR_get_error(), NULL)); rval = -1; return (NULL); } /* * Generate the group key b, which is saved in the e member of * the RSA structure. These values are distributed to all * members of the group, but shielded from all other groups. We * don't use all the parameters, but set the unused ones to a * small number to minimize the file size. */ ctx = BN_CTX_new(); BN_rand(rsa->e, BN_num_bits(rsa->n), -1, 0); /* b */ BN_mod(rsa->e, rsa->e, rsa->n, ctx); BN_copy(rsa->d, BN_value_one()); BN_copy(rsa->p, BN_value_one()); BN_copy(rsa->q, BN_value_one()); BN_copy(rsa->dmp1, BN_value_one()); BN_copy(rsa->dmq1, BN_value_one()); BN_copy(rsa->iqmp, BN_value_one()); /* * Write the GQ parameters as a RSA private key encoded in PEM. * The public and private keys are filled in later. * * n modulus n * e group key b * (remaining values are not used) */ str = fheader("GQpar", trustname); pkey = EVP_PKEY_new(); EVP_PKEY_assign_RSA(pkey, rsa); PEM_write_PrivateKey(str, pkey, passwd2 ? EVP_des_cbc() : NULL, NULL, 0, NULL, passwd2); fclose(str); if (debug) RSA_print_fp(stdout, rsa, 0); fslink(id, trustname); return (pkey); } /* * Update Guillou-Quisquater (GQ) parameters */ EVP_PKEY * /* RSA cuckoo nest */ gen_gqkey( char *id, /* file name id */ EVP_PKEY *gqpar /* GQ parameters */ ) { EVP_PKEY *pkey; /* private key */ RSA *rsa; /* RSA parameters */ BN_CTX *ctx; /* BN working space */ BIGNUM *u, *v, *g, *k, *r, *y; /* BN temps */ FILE *str; u_int temp; /* * Generate GQ keys. Note that the group key b is the e member * of * the GQ parameters. */ fprintf(stderr, "Updating GQ keys (%d bits)...\n", modulus); ctx = BN_CTX_new(); u = BN_new(); v = BN_new(); g = BN_new(); k = BN_new(); r = BN_new(); y = BN_new(); /* * When generating his certificate, Bob rolls random private key * u. */ rsa = gqpar->pkey.rsa; BN_rand(u, BN_num_bits(rsa->n), -1, 0); /* u */ BN_mod(u, u, rsa->n, ctx); BN_mod_inverse(v, u, rsa->n, ctx); /* u^-1 mod n */ BN_mod_mul(k, v, u, rsa->n, ctx); /* * Bob computes public key v = (u^-1)^b, which is saved in an * extension field on his certificate. We check that u^b v = * 1 mod n. */ BN_mod_exp(v, v, rsa->e, rsa->n, ctx); BN_mod_exp(g, u, rsa->e, rsa->n, ctx); /* u^b */ BN_mod_mul(g, g, v, rsa->n, ctx); /* u^b (u^-1)^b */ temp = BN_is_one(g); fprintf(stderr, "Confirm u^b (u^-1)^b = 1 mod n: %s\n", temp ? "yes" : "no"); if (!temp) { BN_free(u); BN_free(v); BN_free(g); BN_free(k); BN_free(r); BN_free(y); BN_CTX_free(ctx); RSA_free(rsa); rval = -1; return (NULL); } BN_copy(rsa->p, u); /* private key */ BN_copy(rsa->q, v); /* public key */ /* * Here is a trial run of the protocol. First, Alice rolls * random r (0 < r < n) and sends it to Bob. She needs only * modulus n from the parameters. */ BN_rand(r, BN_num_bits(rsa->n), -1, 0); /* r */ BN_mod(r, r, rsa->n, ctx); /* * Bob rolls random k (0 < k < n), computes y = k u^r mod n and * g = k^b mod n, then sends (y, g) to Alice. He needs modulus n * from the parameters and his private key u. */ BN_rand(k, BN_num_bits(rsa->n), -1, 0); /* k */ BN_mod(k, k, rsa->n, ctx); BN_mod_exp(y, rsa->p, r, rsa->n, ctx); /* u^r mod n */ BN_mod_mul(y, k, y, rsa->n, ctx); /* y = k u^r mod n */ BN_mod_exp(g, k, rsa->e, rsa->n, ctx); /* g = k^b mod n */ /* * Alice computes v^r y^b mod n and verifies the result is equal * to g. She needs modulus n, generator g and group key b from * the parameters and Bob's public key v = (u^-1)^b from his * certificate. */ BN_mod_exp(v, rsa->q, r, rsa->n, ctx); /* v^r mod n */ BN_mod_exp(y, y, rsa->e, rsa->n, ctx); /* y^b mod n */ BN_mod_mul(y, v, y, rsa->n, ctx); /* v^r y^b mod n */ temp = BN_cmp(y, g); fprintf(stderr, "Confirm g^k = v^r y^b mod n: %s\n", temp == 0 ? "yes" : "no"); BN_CTX_free(ctx); BN_free(u); BN_free(v); BN_free(g); BN_free(k); BN_free(r); BN_free(y); if (temp != 0) { RSA_free(rsa); rval = -1; return (NULL); } /* * Write the GQ parameters and keys as a RSA private key encoded * in PEM. * * n modulus n * e group key b * p private key u * q public key (u^-1)^b * (remaining values are not used) */ str = fheader("GQpar", trustname); pkey = EVP_PKEY_new(); EVP_PKEY_assign_RSA(pkey, rsa); PEM_write_PrivateKey(str, pkey, passwd2 ? EVP_des_cbc() : NULL, NULL, 0, NULL, passwd2); fclose(str); if (debug) RSA_print_fp(stdout, rsa, 0); fslink(id, trustname); return (pkey); } /* * Generate Mu-Varadharajan (MV) parameters and keys * * The Mu-Varadharajan (MV) cryptosystem is useful when servers * broadcast messages to clients, but clients never send messages to * servers. There is one encryption key for the server and a separate * decryption key for each client. It operates something like a * pay-per-view satellite broadcasting system where the session key is * encrypted by the broadcaster and the decryption keys are held in a * tamperproof set-top box. We don't use it this way, but read on. * * The MV parameters and private encryption key hide in a DSA cuckoo * structure which uses the same parameters, but generated in a * different way. The values are used in an encryption scheme similar to * El Gamal cryptography and a polynomial formed from the expansion of * product terms (x - x[j]), as described in Mu, Y., and V. * Varadharajan: Robust and Secure Broadcasting, Proc. Indocrypt 2001, * 223-231. The paper has significant errors and serious omissions. * * Let q be the product of n distinct primes s'[j] (j = 1...n), where * each s'[j] has m significant bits. Let p be a prime p = 2 * q + 1, so * that q and each s'[j] divide p - 1 and p has M = n * m + 1 * significant bits. Let g be a generator of Zp; that is, gcd(g, p - 1) * = 1 and g^q = 1 mod p. We do modular arithmetic over Zq and then * project into Zp* as exponents of g. Sometimes we have to compute an * inverse b^-1 of random b in Zq, but for that purpose we require * gcd(b, q) = 1. We expect M to be in the 500-bit range and n * relatively small, like 30. Associated with each s'[j] is an element * s[j] such that s[j] s'[j] = s'[j] mod q. We find s[j] as the quotient * (q + s'[j]) / s'[j]. These are the parameters of the scheme and they * are expensive to compute. * * We set up an instance of the scheme as follows. A set of random * values x[j] mod q (j = 1...n), are generated as the zeros of a * polynomial of order n. The product terms (x - x[j]) are expanded to * form coefficients a[i] mod q (i = 0...n) in powers of x. These are * used as exponents of the generator g mod p to generate the private * encryption key A. The pair (gbar, ghat) of public server keys and the * pairs (xbar[j], xhat[j]) (j = 1...n) of private client keys are used * to construct the decryption keys. The devil is in the details. * * This routine generates a private encryption file including the * private encryption key E and public key (gbar, ghat). It then * generates decryption files including the private key (xbar[j], * xhat[j]) for each client. E is a permutation that encrypts a block * y = E x. The jth client computes the inverse permutation E^-1 = * gbar^xhat[j] ghat^xbar[j] and decrypts the block x = E^-1 y. * * The distinguishing characteristic of this scheme is the capability to * revoke keys. Included in the calculation of E, gbar and ghat is the * product s = prod(s'[j]) (j = 1...n) above. If the factor s'[j] is * subsequently removed from the product and E, gbar and ghat * recomputed, the jth client will no longer be able to compute E^-1 and * thus unable to decrypt the block. */ EVP_PKEY * /* DSA cuckoo nest */ gen_mv( char *id /* file name id */ ) { EVP_PKEY *pkey, *pkey1; /* private key */ DSA *dsa; /* DSA parameters */ DSA *sdsa; /* DSA parameters */ BN_CTX *ctx; /* BN working space */ BIGNUM **x; /* polynomial zeros vector */ BIGNUM **a; /* polynomial coefficient vector */ BIGNUM **g; /* public key vector */ BIGNUM **s, **s1; /* private enabling keys */ BIGNUM **xbar, **xhat; /* private keys vector */ BIGNUM *b; /* group key */ BIGNUM *b1; /* inverse group key */ BIGNUM *ss; /* enabling key */ BIGNUM *biga; /* master encryption key */ BIGNUM *bige; /* session encryption key */ BIGNUM *gbar, *ghat; /* public key */ BIGNUM *u, *v, *w; /* BN scratch */ int i, j, n; FILE *str; u_int temp; char ident[20]; /* * Generate MV parameters. * * The object is to generate a multiplicative group Zp* modulo a * prime p and a subset Zq mod q, where q is the product of n * distinct primes s'[j] (j = 1...n) and q divides p - 1. We * first generate n distinct primes, which may have to be * regenerated later. As a practical matter, it is tough to find * more than 31 distinct primes for modulus 512 or 61 primes for * modulus 1024. The latter can take several hundred iterations * and several minutes on a Sun Blade 1000. */ n = nkeys; fprintf(stderr, "Generating MV parameters for %d keys (%d bits)...\n", n, modulus / n); ctx = BN_CTX_new(); u = BN_new(); v = BN_new(); w = BN_new(); b = BN_new(); b1 = BN_new(); dsa = DSA_new(); dsa->p = BN_new(); dsa->q = BN_new(); dsa->g = BN_new(); s = malloc((n + 1) * sizeof(BIGNUM)); s1 = malloc((n + 1) * sizeof(BIGNUM)); for (j = 1; j <= n; j++) s1[j] = BN_new(); temp = 0; for (j = 1; j <= n; j++) { while (1) { fprintf(stderr, "Birthdays %d\r", temp); BN_generate_prime(s1[j], modulus / n, 0, NULL, NULL, NULL, NULL); for (i = 1; i < j; i++) { if (BN_cmp(s1[i], s1[j]) == 0) break; } if (i == j) break; temp++; } } fprintf(stderr, "Birthday keys rejected %d\n", temp); /* * Compute the modulus q as the product of the primes. Compute * the modulus p as 2 * q + 1 and test p for primality. If p * is composite, replace one of the primes with a new distinct * one and try again. Note that q will hardly be a secret since * we have to reveal p to servers and clients. However, * factoring q to find the primes should be adequately hard, as * this is the same problem considered hard in RSA. Question: is * it as hard to find n small prime factors totalling n bits as * it is to find two large prime factors totalling n bits? * Remember, the bad guy doesn't know n. */ temp = 0; while (1) { fprintf(stderr, "Duplicate keys rejected %d\r", ++temp); BN_one(dsa->q); for (j = 1; j <= n; j++) BN_mul(dsa->q, dsa->q, s1[j], ctx); BN_copy(dsa->p, dsa->q); BN_add(dsa->p, dsa->p, dsa->p); BN_add_word(dsa->p, 1); if (BN_is_prime(dsa->p, BN_prime_checks, NULL, ctx, NULL)) break; j = temp % n + 1; while (1) { BN_generate_prime(u, modulus / n, 0, 0, NULL, NULL, NULL); for (i = 1; i <= n; i++) { if (BN_cmp(u, s1[i]) == 0) break; } if (i > n) break; } BN_copy(s1[j], u); } fprintf(stderr, "Duplicate keys rejected %d\n", temp); /* * Compute the generator g using a random roll such that * gcd(g, p - 1) = 1 and g^q = 1. This is a generator of p, not * q. */ BN_copy(v, dsa->p); BN_sub_word(v, 1); while (1) { BN_rand(dsa->g, BN_num_bits(dsa->p) - 1, 0, 0); BN_mod(dsa->g, dsa->g, dsa->p, ctx); BN_gcd(u, dsa->g, v, ctx); if (!BN_is_one(u)) continue; BN_mod_exp(u, dsa->g, dsa->q, dsa->p, ctx); if (BN_is_one(u)) break; } /* * Compute s[j] such that s[j] * s'[j] = s'[j] for all j. The * easy way to do this is to compute q + s'[j] and divide the * result by s'[j]. Exercise for the student: prove the * remainder is always zero. */ for (j = 1; j <= n; j++) { s[j] = BN_new(); BN_add(s[j], dsa->q, s1[j]); BN_div(s[j], u, s[j], s1[j], ctx); } /* * Setup is now complete. Roll random polynomial roots x[j] * (0 < x[j] < q) for all j. While it may not be strictly * necessary, Make sure each root has no factors in common with * q. */ fprintf(stderr, "Generating polynomial coefficients for %d roots (%d bits)\n", n, BN_num_bits(dsa->q)); x = malloc((n + 1) * sizeof(BIGNUM)); for (j = 1; j <= n; j++) { x[j] = BN_new(); while (1) { BN_rand(x[j], BN_num_bits(dsa->q), 0, 0); BN_mod(x[j], x[j], dsa->q, ctx); BN_gcd(u, x[j], dsa->q, ctx); if (BN_is_one(u)) break; } } /* * Generate polynomial coefficients a[i] (i = 0...n) from the * expansion of root products (x - x[j]) mod q for all j. The * method is a present from Charlie Boncelet. */ a = malloc((n + 1) * sizeof(BIGNUM)); for (i = 0; i <= n; i++) { a[i] = BN_new(); BN_one(a[i]); } for (j = 1; j <= n; j++) { BN_zero(w); for (i = 0; i < j; i++) { BN_copy(u, dsa->q); BN_mod_mul(v, a[i], x[j], dsa->q, ctx); BN_sub(u, u, v); BN_add(u, u, w); BN_copy(w, a[i]); BN_mod(a[i], u, dsa->q, ctx); } } /* * Generate g[i] = g^a[i] mod p for all i and the generator g. */ fprintf(stderr, "Generating g[i] parameters\n"); g = malloc((n + 1) * sizeof(BIGNUM)); for (i = 0; i <= n; i++) { g[i] = BN_new(); BN_mod_exp(g[i], dsa->g, a[i], dsa->p, ctx); } /* * Verify prod(g[i]^(a[i] x[j]^i)) = 1 for all i, j; otherwise, * exit. Note the a[i] x[j]^i exponent is computed mod q, but * the g[i] is computed mod p. also note the expression given in * the paper is incorrect. */ temp = 1; for (j = 1; j <= n; j++) { BN_one(u); for (i = 0; i <= n; i++) { BN_set_word(v, i); BN_mod_exp(v, x[j], v, dsa->q, ctx); BN_mod_mul(v, v, a[i], dsa->q, ctx); BN_mod_exp(v, dsa->g, v, dsa->p, ctx); BN_mod_mul(u, u, v, dsa->p, ctx); } if (!BN_is_one(u)) temp = 0; } fprintf(stderr, "Confirm prod(g[i]^(x[j]^i)) = 1 for all i, j: %s\n", temp ? "yes" : "no"); if (!temp) { rval = -1; return (NULL); } /* * Make private encryption key A. Keep it around for awhile, * since it is expensive to compute. */ biga = BN_new(); BN_one(biga); for (j = 1; j <= n; j++) { for (i = 0; i < n; i++) { BN_set_word(v, i); BN_mod_exp(v, x[j], v, dsa->q, ctx); BN_mod_exp(v, g[i], v, dsa->p, ctx); BN_mod_mul(biga, biga, v, dsa->p, ctx); } } /* * Roll private random group key b mod q (0 < b < q), where * gcd(b, q) = 1 to guarantee b^1 exists, then compute b^-1 * mod q. If b is changed, the client keys must be recomputed. */ while (1) { BN_rand(b, BN_num_bits(dsa->q), 0, 0); BN_mod(b, b, dsa->q, ctx); BN_gcd(u, b, dsa->q, ctx); if (BN_is_one(u)) break; } BN_mod_inverse(b1, b, dsa->q, ctx); /* * Make private client keys (xbar[j], xhat[j]) for all j. Note * that the keys for the jth client involve s[j], but not s'[j] * or the product s = prod(s'[j]) mod q, which is the enabling * key. */ xbar = malloc((n + 1) * sizeof(BIGNUM)); xhat = malloc((n + 1) * sizeof(BIGNUM)); for (j = 1; j <= n; j++) { xbar[j] = BN_new(); xhat[j] = BN_new(); BN_zero(xbar[j]); BN_set_word(v, n); for (i = 1; i <= n; i++) { if (i == j) continue; BN_mod_exp(u, x[i], v, dsa->q, ctx); BN_add(xbar[j], xbar[j], u); } BN_mod_mul(xbar[j], xbar[j], b1, dsa->q, ctx); BN_mod_exp(xhat[j], x[j], v, dsa->q, ctx); BN_mod_mul(xhat[j], xhat[j], s[j], dsa->q, ctx); } /* * The enabling key is initially q by construction. We can * revoke client j by dividing q by s'[j]. The quotient becomes * the enabling key s. Note we always have to revoke one key; * otherwise, the plaintext and cryptotext would be identical. */ ss = BN_new(); BN_copy(ss, dsa->q); BN_div(ss, u, dsa->q, s1[n], ctx); /* * Make private server encryption key E = A^s and public server * keys gbar = g^s mod p and ghat = g^(s b) mod p. The (gbar, * ghat) is the public key provided to the server, which uses it * to compute the session encryption key and public key included * in its messages. These values must be regenerated if the * enabling key is changed. */ bige = BN_new(); gbar = BN_new(); ghat = BN_new(); BN_mod_exp(bige, biga, ss, dsa->p, ctx); BN_mod_exp(gbar, dsa->g, ss, dsa->p, ctx); BN_mod_mul(v, ss, b, dsa->q, ctx); BN_mod_exp(ghat, dsa->g, v, dsa->p, ctx); /* * We produce the key media in three steps. The first step is to * generate the private values that do not depend on the * enabling key. These include the server values p, q, g, b, A * and the client values s'[j], xbar[j] and xhat[j] for each j. * The p, xbar[j] and xhat[j] values are encoded in private * files which are distributed to respective clients. The p, q, * g, A and s'[j] values (will be) written to a secret file to * be read back later. * * The secret file (will be) read back at some later time to * enable/disable individual keys and generate/regenerate the * enabling key s. The p, q, E, gbar and ghat values are written * to a secret file to be read back later by the server. * * The server reads the secret file and rolls the session key * k, which is used only once, then computes E^k, gbar^k and * ghat^k. The E^k is the session encryption key. The encrypted * data, gbar^k and ghat^k are transmtted to clients in an * extension field. The client receives the message and computes * x = (gbar^k)^xbar[j] (ghat^k)^xhat[j], finds the session * encryption key E^k as the inverse x^-1 and decrypts the data. */ BN_copy(dsa->g, bige); dsa->priv_key = BN_dup(gbar); dsa->pub_key = BN_dup(ghat); /* * Write the MV server parameters and keys as a DSA private key * encoded in PEM. * * p modulus p * q modulus q (used only to generate k) * g E mod p * priv_key gbar mod p * pub_key ghat mod p */ str = fheader("MVpar", trustname); pkey = EVP_PKEY_new(); EVP_PKEY_assign_DSA(pkey, dsa); PEM_write_PrivateKey(str, pkey, passwd2 ? EVP_des_cbc() : NULL, NULL, 0, NULL, passwd2); fclose(str); if (debug) DSA_print_fp(stdout, dsa, 0); fslink(id, trustname); /* * Write the parameters and private key (xbar[j], xhat[j]) for * all j as a DSA private key encoded in PEM. It is used only by * the designated recipient(s) who pay a suitably outrageous fee * for its use. */ sdsa = DSA_new(); sdsa->p = BN_dup(dsa->p); sdsa->q = BN_dup(BN_value_one()); sdsa->g = BN_dup(BN_value_one()); sdsa->priv_key = BN_new(); sdsa->pub_key = BN_new(); for (j = 1; j <= n; j++) { BN_copy(sdsa->priv_key, xbar[j]); BN_copy(sdsa->pub_key, xhat[j]); BN_mod_exp(v, dsa->priv_key, sdsa->pub_key, dsa->p, ctx); BN_mod_exp(u, dsa->pub_key, sdsa->priv_key, dsa->p, ctx); BN_mod_mul(u, u, v, dsa->p, ctx); BN_mod_mul(u, u, dsa->g, dsa->p, ctx); BN_free(xbar[j]); BN_free(xhat[j]); BN_free(x[j]); BN_free(s[j]); BN_free(s1[j]); if (!BN_is_one(u)) { fprintf(stderr, "Revoke key %d\n", j); continue; } /* * Write the client parameters as a DSA private key * encoded in PEM. We don't make links for these. * * p modulus p * priv_key xbar[j] mod q * pub_key xhat[j] mod q * (remaining values are not used) */ sprintf(ident, "MVkey%d", j); str = fheader(ident, trustname); pkey1 = EVP_PKEY_new(); EVP_PKEY_set1_DSA(pkey1, sdsa); PEM_write_PrivateKey(str, pkey1, passwd2 ? EVP_des_cbc() : NULL, NULL, 0, NULL, passwd2); fclose(str); fprintf(stderr, "ntpkey_%s_%s.%lu\n", ident, trustname, epoch + JAN_1970); if (debug) DSA_print_fp(stdout, sdsa, 0); EVP_PKEY_free(pkey1); } /* * Free the countries. */ for (i = 0; i <= n; i++) { BN_free(a[i]); BN_free(g[i]); } BN_free(u); BN_free(v); BN_free(w); BN_CTX_free(ctx); BN_free(b); BN_free(b1); BN_free(biga); BN_free(bige); BN_free(ss); BN_free(gbar); BN_free(ghat); DSA_free(sdsa); /* * Free the world. */ free(x); free(a); free(g); free(s); free(s1); free(xbar); free(xhat); return (pkey); } /* * Generate X509v3 scertificate. * * The certificate consists of the version number, serial number, * validity interval, issuer name, subject name and public key. For a * self-signed certificate, the issuer name is the same as the subject * name and these items are signed using the subject private key. The * validity interval extends from the current time to the same time one * year hence. For NTP purposes, it is convenient to use the NTP seconds * of the current time as the serial number. */ int x509 ( EVP_PKEY *pkey, /* generic signature algorithm */ const EVP_MD *md, /* generic digest algorithm */ char *gqpub, /* identity extension (hex string) */ char *exten /* private cert extension */ ) { X509 *cert; /* X509 certificate */ X509_NAME *subj; /* distinguished (common) name */ X509_EXTENSION *ex; /* X509v3 extension */ FILE *str; /* file handle */ ASN1_INTEGER *serial; /* serial number */ const char *id; /* digest/signature scheme name */ char pathbuf[MAXFILENAME + 1]; /* * Generate X509 self-signed certificate. * * Set the certificate serial to the NTP seconds for grins. Set * the version to 3. Set the subject name and issuer name to the * subject name in the request. Set the initial validity to the * current time and the final validity one year hence. */ id = OBJ_nid2sn(md->pkey_type); fprintf(stderr, "Generating certificate %s\n", id); cert = X509_new(); X509_set_version(cert, 2L); serial = ASN1_INTEGER_new(); ASN1_INTEGER_set(serial, epoch + JAN_1970); X509_set_serialNumber(cert, serial); ASN1_INTEGER_free(serial); X509_time_adj(X509_get_notBefore(cert), 0L, &epoch); X509_time_adj(X509_get_notAfter(cert), YEAR, &epoch); subj = X509_get_subject_name(cert); X509_NAME_add_entry_by_txt(subj, "commonName", MBSTRING_ASC, (unsigned char *) hostname, strlen(hostname), -1, 0); subj = X509_get_issuer_name(cert); X509_NAME_add_entry_by_txt(subj, "commonName", MBSTRING_ASC, (unsigned char *) trustname, strlen(trustname), -1, 0); if (!X509_set_pubkey(cert, pkey)) { fprintf(stderr, "Assign key fails\n%s\n", ERR_error_string(ERR_get_error(), NULL)); X509_free(cert); rval = -1; return (0); } /* * Add X509v3 extensions if present. These represent the minimum * set defined in RFC3280 less the certificate_policy extension, * which is seriously obfuscated in OpenSSL. */ /* * The basic_constraints extension CA:TRUE allows servers to * sign client certficitates. */ fprintf(stderr, "%s: %s\n", LN_basic_constraints, BASIC_CONSTRAINTS); ex = X509V3_EXT_conf_nid(NULL, NULL, NID_basic_constraints, BASIC_CONSTRAINTS); if (!X509_add_ext(cert, ex, -1)) { fprintf(stderr, "Add extension field fails\n%s\n", ERR_error_string(ERR_get_error(), NULL)); rval = -1; return (0); } X509_EXTENSION_free(ex); /* * The key_usage extension designates the purposes the key can * be used for. */ fprintf(stderr, "%s: %s\n", LN_key_usage, KEY_USAGE); ex = X509V3_EXT_conf_nid(NULL, NULL, NID_key_usage, KEY_USAGE); if (!X509_add_ext(cert, ex, -1)) { fprintf(stderr, "Add extension field fails\n%s\n", ERR_error_string(ERR_get_error(), NULL)); rval = -1; return (0); } X509_EXTENSION_free(ex); /* * The subject_key_identifier is used for the GQ public key. * This should not be controversial. */ if (gqpub != NULL) { fprintf(stderr, "%s\n", LN_subject_key_identifier); ex = X509V3_EXT_conf_nid(NULL, NULL, NID_subject_key_identifier, gqpub); if (!X509_add_ext(cert, ex, -1)) { fprintf(stderr, "Add extension field fails\n%s\n", ERR_error_string(ERR_get_error(), NULL)); rval = -1; return (0); } X509_EXTENSION_free(ex); } /* * The extended key usage extension is used for special purpose * here. The semantics probably do not conform to the designer's * intent and will likely change in future. * * "trustRoot" designates a root authority * "private" designates a private certificate */ if (exten != NULL) { fprintf(stderr, "%s: %s\n", LN_ext_key_usage, exten); ex = X509V3_EXT_conf_nid(NULL, NULL, NID_ext_key_usage, exten); if (!X509_add_ext(cert, ex, -1)) { fprintf(stderr, "Add extension field fails\n%s\n", ERR_error_string(ERR_get_error(), NULL)); rval = -1; return (0); } X509_EXTENSION_free(ex); } /* * Sign and verify. */ X509_sign(cert, pkey, md); if (!X509_verify(cert, pkey)) { fprintf(stderr, "Verify %s certificate fails\n%s\n", id, ERR_error_string(ERR_get_error(), NULL)); X509_free(cert); rval = -1; return (0); } /* * Write the certificate encoded in PEM. */ sprintf(pathbuf, "%scert", id); str = fheader(pathbuf, hostname); PEM_write_X509(str, cert); fclose(str); if (debug) X509_print_fp(stdout, cert); X509_free(cert); fslink("cert", hostname); return (1); } #if 0 /* asn2ntp is not used */ /* * asn2ntp - convert ASN1_TIME time structure to NTP time */ u_long asn2ntp ( ASN1_TIME *asn1time /* pointer to ASN1_TIME structure */ ) { char *v; /* pointer to ASN1_TIME string */ struct tm tm; /* time decode structure time */ /* * Extract time string YYMMDDHHMMSSZ from ASN.1 time structure. * Note that the YY, MM, DD fields start with one, the HH, MM, * SS fiels start with zero and the Z character should be 'Z' * for UTC. Also note that years less than 50 map to years * greater than 100. Dontcha love ASN.1? */ if (asn1time->length > 13) return (-1); v = (char *)asn1time->data; tm.tm_year = (v[0] - '0') * 10 + v[1] - '0'; if (tm.tm_year < 50) tm.tm_year += 100; tm.tm_mon = (v[2] - '0') * 10 + v[3] - '0' - 1; tm.tm_mday = (v[4] - '0') * 10 + v[5] - '0'; tm.tm_hour = (v[6] - '0') * 10 + v[7] - '0'; tm.tm_min = (v[8] - '0') * 10 + v[9] - '0'; tm.tm_sec = (v[10] - '0') * 10 + v[11] - '0'; tm.tm_wday = 0; tm.tm_yday = 0; tm.tm_isdst = 0; return (mktime(&tm) + JAN_1970); } #endif /* * Callback routine */ void cb ( int n1, /* arg 1 */ int n2, /* arg 2 */ void *chr /* arg 3 */ ) { switch (n1) { case 0: d0++; fprintf(stderr, "%s %d %d %lu\r", (char *)chr, n1, n2, d0); break; case 1: d1++; fprintf(stderr, "%s\t\t%d %d %lu\r", (char *)chr, n1, n2, d1); break; case 2: d2++; fprintf(stderr, "%s\t\t\t\t%d %d %lu\r", (char *)chr, n1, n2, d2); break; case 3: d3++; fprintf(stderr, "%s\t\t\t\t\t\t%d %d %lu\r", (char *)chr, n1, n2, d3); break; } } /* * Generate key */ EVP_PKEY * /* public/private key pair */ genkey( char *type, /* key type (RSA or DSA) */ char *id /* file name id */ ) { if (type == NULL) return (NULL); if (strcmp(type, "RSA") == 0) return (gen_rsa(id)); else if (strcmp(type, "DSA") == 0) return (gen_dsa(id)); fprintf(stderr, "Invalid %s key type %s\n", id, type); rval = -1; return (NULL); } #endif /* OPENSSL */ /* * Generate file header */ FILE * fheader ( const char *id, /* file name id */ const char *name /* owner name */ ) { FILE *str; /* file handle */ sprintf(filename, "ntpkey_%s_%s.%lu", id, name, epoch + JAN_1970); if ((str = fopen(filename, "w")) == NULL) { perror("Write"); exit (-1); } fprintf(str, "# %s\n# %s", filename, ctime(&epoch)); return (str); } /* * Generate symbolic links */ void fslink( const char *id, /* file name id */ const char *name /* owner name */ ) { char linkname[MAXFILENAME]; /* link name */ int temp; sprintf(linkname, "ntpkey_%s_%s", id, name); remove(linkname); temp = symlink(filename, linkname); if (temp < 0) perror(id); fprintf(stderr, "Generating new %s file and link\n", id); fprintf(stderr, "%s->%s\n", linkname, filename); }