1/*
2 * authreadkeys.c - routines to support the reading of the key file
3 */
4#include <config.h>
5#include <stdio.h>
6#include <ctype.h>
7
8#include "ntp_fp.h"
9#include "ntp.h"
10#include "ntp_syslog.h"
11#include "ntp_stdlib.h"
12
13#ifdef OPENSSL
14#include "openssl/objects.h"
15#endif /* OPENSSL */
16
17/* Forwards */
18static char *nexttok (char **);
19
20/*
21 * nexttok - basic internal tokenizing routine
22 */
23static char *
24nexttok(
25	char	**str
26	)
27{
28	register char *cp;
29	char *starttok;
30
31	cp = *str;
32
33	/*
34	 * Space past white space
35	 */
36	while (*cp == ' ' || *cp == '\t')
37	    cp++;
38
39	/*
40	 * Save this and space to end of token
41	 */
42	starttok = cp;
43	while (*cp != '\0' && *cp != '\n' && *cp != ' '
44	       && *cp != '\t' && *cp != '#')
45	    cp++;
46
47	/*
48	 * If token length is zero return an error, else set end of
49	 * token to zero and return start.
50	 */
51	if (starttok == cp)
52	    return (NULL);
53
54	if (*cp == ' ' || *cp == '\t')
55	    *cp++ = '\0';
56	else
57	    *cp = '\0';
58
59	*str = cp;
60	return starttok;
61}
62
63
64/*
65 * authreadkeys - (re)read keys from a file.
66 */
67int
68authreadkeys(
69	const char *file
70	)
71{
72	FILE	*fp;
73	char	*line;
74	char	*token;
75	keyid_t	keyno;
76	int	keytype;
77	char	buf[512];		/* lots of room for line */
78	u_char	keystr[20];
79	int	len;
80	int	j;
81
82	/*
83	 * Open file.  Complain and return if it can't be opened.
84	 */
85	fp = fopen(file, "r");
86	if (fp == NULL) {
87		msyslog(LOG_ERR, "authreadkeys: file %s: %m",
88		    file);
89		return (0);
90	}
91	INIT_SSL();
92
93	/*
94	 * Remove all existing keys
95	 */
96	auth_delkeys();
97
98	/*
99	 * Now read lines from the file, looking for key entries
100	 */
101	while ((line = fgets(buf, sizeof buf, fp)) != NULL) {
102		token = nexttok(&line);
103		if (token == NULL)
104			continue;
105
106		/*
107		 * First is key number.  See if it is okay.
108		 */
109		keyno = atoi(token);
110		if (keyno == 0) {
111			msyslog(LOG_ERR,
112			    "authreadkeys: cannot change key %s", token);
113			continue;
114		}
115
116		if (keyno > NTP_MAXKEY) {
117			msyslog(LOG_ERR,
118			    "authreadkeys: key %s > %d reserved for Autokey",
119			    token, NTP_MAXKEY);
120			continue;
121		}
122
123		/*
124		 * Next is keytype. See if that is all right.
125		 */
126		token = nexttok(&line);
127		if (token == NULL) {
128			msyslog(LOG_ERR,
129			    "authreadkeys: no key type for key %d", keyno);
130			continue;
131		}
132#ifdef OPENSSL
133		/*
134		 * The key type is the NID used by the message digest
135		 * algorithm. There are a number of inconsistencies in
136		 * the OpenSSL database. We attempt to discover them
137		 * here and prevent use of inconsistent data later.
138		 */
139		keytype = keytype_from_text(token, NULL);
140		if (keytype == 0) {
141			msyslog(LOG_ERR,
142			    "authreadkeys: invalid type for key %d", keyno);
143			continue;
144		}
145		if (EVP_get_digestbynid(keytype) == NULL) {
146			msyslog(LOG_ERR,
147			    "authreadkeys: no algorithm for key %d", keyno);
148			continue;
149		}
150#else /* OPENSSL */
151
152		/*
153		 * The key type is unused, but is required to be 'M' or
154		 * 'm' for compatibility.
155		 */
156		if (!(*token == 'M' || *token == 'm')) {
157			msyslog(LOG_ERR,
158			    "authreadkeys: invalid type for key %d", keyno);
159			continue;
160		}
161		keytype = KEY_TYPE_MD5;
162#endif /* OPENSSL */
163
164		/*
165		 * Finally, get key and insert it. If it is longer than 20
166		 * characters, it is a binary string encoded in hex;
167		 * otherwise, it is a text string of printable ASCII
168		 * characters.
169		 */
170		token = nexttok(&line);
171		if (token == NULL) {
172			msyslog(LOG_ERR,
173			    "authreadkeys: no key for key %d", keyno);
174			continue;
175		}
176		len = strlen(token);
177		if (len <= 20) {
178			MD5auth_setkey(keyno, keytype, (u_char *)token, len);
179		} else {
180			char	hex[] = "0123456789abcdef";
181			u_char	temp;
182			char	*ptr;
183			int	jlim;
184
185			jlim = min(len, 2 * sizeof(keystr));
186			for (j = 0; j < jlim; j++) {
187				ptr = strchr(hex, tolower(token[j]));
188				if (ptr == NULL) {
189					msyslog(LOG_ERR,
190					    "authreadkeys: invalid hex digit for key %d", keyno);
191					continue;
192				}
193				temp = (u_char)(ptr - hex);
194				if (j & 1)
195					keystr[j / 2] |= temp;
196				else
197					keystr[j / 2] = temp << 4;
198			}
199			MD5auth_setkey(keyno, keytype, keystr, jlim / 2);
200		}
201	}
202	fclose(fp);
203	return (1);
204}
205