xref: /freebsd/contrib/ntp/libntp/authreadkeys.c (revision 88f578841fd49ff796b62a8d3531bd73afd1a88e)
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 */
19 static char *nexttok (char **);
20 
21 /*
22  * nexttok - basic internal tokenizing routine
23  */
24 static char *
25 nexttok(
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  */
68 int
69 authreadkeys(
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