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