1 #include <sys/types.h>
2 #include <sys/stat.h>
3 #include <fcntl.h>
4 #include <pwd.h>
5 #include <limits.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <openssl/evp.h>
10 #include <openssl/bn.h>
11 #include <openssl/x509.h>
12 
13 /* how to read the authorized_keys file and the key?
14  * see openssh source code auth2-pubkey.c user_key_allowed2
15  * and misc.c read_keyfile_line and key.c
16  */
17 
18 #define OPENSSH_LINE_MAX 16384	/* from openssh SSH_MAX_PUBKEY_BYTES */
19 
20 #if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined (LIBRESSL_VERSION_NUMBER)
RSA_get0_key(const RSA * r,const BIGNUM ** n,const BIGNUM ** e,const BIGNUM ** d)21 void RSA_get0_key(const RSA *r,
22                  const BIGNUM **n, const BIGNUM **e, const BIGNUM **d)
23 {
24 	if (n != NULL)
25 		*n = r->n;
26 	if (e != NULL)
27 		*e = r->e;
28 	if (d != NULL)
29 		*d = r->d;
30 }
31 
RSA_set0_key(RSA * r,BIGNUM * n,BIGNUM * e,BIGNUM * d)32 int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d)
33 {
34 	/* If the fields n and e in r are NULL, the corresponding input
35 	* parameters MUST be non-NULL for n and e.  d may be
36 	* left NULL (in case only the public key is used).
37 	*/
38 	if ((r->n == NULL && n == NULL)
39 	|| (r->e == NULL && e == NULL))
40 		return 0;
41 
42 	if (n != NULL) {
43 		BN_free(r->n);
44 		r->n = n;
45 	}
46 	if (e != NULL) {
47 		BN_free(r->e);
48 		r->e = e;
49 	}
50 	if (d != NULL) {
51 		BN_free(r->d);
52 		r->d = d;
53 	}
54 
55 	return 1;
56 }
57 
58 #endif
59 
ssh1_line_to_key(char * line)60 static EVP_PKEY *ssh1_line_to_key(char *line)
61 {
62 	EVP_PKEY *key;
63 	RSA *rsa;
64 	char *b, *e, *m, *c;
65 	BIGNUM *rsa_e, *rsa_n;
66 
67 	key = EVP_PKEY_new();
68 	if (!key)
69 		return NULL;
70 
71 	rsa = RSA_new();
72 
73 	if (!rsa)
74 		goto err;
75 
76 	/* first digitstring: the bits */
77 	b = line;
78 
79 	/* second digitstring: the exponent */
80 	/* skip all digits */
81 	for (e = b; *e >= '0' && *e <= '0'; e++) ;
82 
83 	/* must be a whitespace */
84 	if (*e != ' ' && *e != '\t')
85 		return NULL;
86 
87 	/* cut the string in two part */
88 	*e = 0;
89 	e++;
90 
91 	/* skip more whitespace */
92 	while (*e == ' ' || *e == '\t')
93 		e++;
94 
95 	/* third digitstring: the modulus */
96 	/* skip all digits */
97 	for (m = e; *m >= '0' && *m <= '0'; m++) ;
98 
99 	/* must be a whitespace */
100 	if (*m != ' ' && *m != '\t')
101 		return NULL;
102 
103 	/* cut the string in two part */
104 	*m = 0;
105 	m++;
106 
107 	/* skip more whitespace */
108 	while (*m == ' ' || *m == '\t')
109 		m++;
110 
111 	/* look for a comment after the modulus */
112 	for (c = m; *c >= '0' && *c <= '0'; c++) ;
113 
114 	/* could be a whitespace or end of line */
115 	if (*c != ' ' && *c != '\t' && *c != '\n' && *c != '\r' && *c != 0)
116 		return NULL;
117 
118 	if (*c == ' ' || *c == '\t') {
119 		*c = 0;
120 		c++;
121 
122 		/* skip more whitespace */
123 		while (*c == ' ' || *c == '\t')
124 			c++;
125 
126 		if (*c && *c != '\r' && *c != '\n') {
127 			/* we have a comment */
128 		} else {
129 			c = NULL;
130 		}
131 
132 	} else {
133 		*c = 0;
134 		c = NULL;
135 	}
136 
137 	/* ok, now we have b e m pointing to pure digit
138 	 * null terminated strings and maybe c pointing to a comment */
139 
140 	BN_dec2bn(&rsa_e, e);
141 	BN_dec2bn(&rsa_n, m);
142 	if (!RSA_set0_key(rsa, rsa_n, rsa_e, NULL))
143 		goto err;
144 
145 	EVP_PKEY_assign_RSA(key, rsa);
146 	return key;
147 
148       err:
149 	EVP_PKEY_free(key);
150 	return NULL;
151 }
152 
153 extern int sc_base64_decode(const char *in, unsigned char *out, size_t outlen);
154 
ssh2_line_to_key(char * line)155 static EVP_PKEY *ssh2_line_to_key(char *line)
156 {
157 	EVP_PKEY *key;
158 	RSA *rsa;
159 	BIGNUM *rsa_e, *rsa_n;
160 	unsigned char decoded[OPENSSH_LINE_MAX];
161 	int len;
162 
163 	char *b, *c;
164 	int i;
165 
166 	/* find the mime-blob */
167 	b = line;
168 
169 	if (!b)
170 		return NULL;
171 
172 	/* find the first whitespace */
173 	while (*b && *b != ' ')
174 		b++;
175 
176 	/* skip that whitespace */
177 	b++;
178 
179 	/* find the end of the blob / comment */
180 	for (c = b; *c && *c != ' ' && 'c' != '\t' && *c != '\r'
181 	     && *c != '\n'; c++) ;
182 
183 	*c = 0;
184 
185 	/* decode binary data */
186 	if (sc_base64_decode(b, decoded, OPENSSH_LINE_MAX) < 0)
187 		return NULL;
188 
189 	i = 0;
190 
191 	/* get integer from blob */
192 	len =
193 	    (decoded[i] << 24) + (decoded[i + 1] << 16) +
194 	    (decoded[i + 2] << 8) + (decoded[i + 3]);
195 	i += 4;
196 
197 	/* now: key_from_blob */
198 	if (strncmp((char *)&decoded[i], "ssh-rsa", 7) != 0)
199 		return NULL;
200 
201 	i += len;
202 
203 	/* to prevent access beyond 'decoded' array, index 'i' must be always checked */
204 	if ( i + 4 > OPENSSH_LINE_MAX )
205 		return NULL;
206 	/* get integer from blob */
207 	len =
208 	    (decoded[i] << 24) + (decoded[i + 1] << 16) +
209 	    (decoded[i + 2] << 8) + (decoded[i + 3]);
210 	i += 4;
211 
212 	if ( i + len > OPENSSH_LINE_MAX )
213 		return NULL;
214 	/* get bignum */
215 	rsa_e = BN_bin2bn(decoded + i, len, NULL);
216 	i += len;
217 
218 	if ( i + 4 > OPENSSH_LINE_MAX )
219 		return NULL;
220 	/* get integer from blob */
221 	len =
222 	    (decoded[i] << 24) + (decoded[i + 1] << 16) +
223 	    (decoded[i + 2] << 8) + (decoded[i + 3]);
224 	i += 4;
225 
226 	if ( i + len > OPENSSH_LINE_MAX )
227 		return NULL;
228 	/* get bignum */
229 	rsa_n = BN_bin2bn(decoded + i, len, NULL);
230 
231 	key = EVP_PKEY_new();
232 	rsa = RSA_new();
233 
234 	/* set e and n */
235 	if (!RSA_set0_key(rsa, rsa_n, rsa_e, NULL)) {
236 		EVP_PKEY_free(key);
237 		RSA_free(rsa);
238 		return NULL;
239 	}
240 
241 	EVP_PKEY_assign_RSA(key, rsa);
242 	return key;
243 }
244 
ssh_nistp_line_to_key(char * line)245 static EVP_PKEY *ssh_nistp_line_to_key(char *line)
246 {
247 	EVP_PKEY *key;
248 	EC_KEY *ec_key;
249 	BIGNUM *x;
250 	BIGNUM *y;
251 
252 	unsigned char decoded[OPENSSH_LINE_MAX];
253 	int len;
254 	int flen;
255 
256 	char *b, *c;
257 	int i;
258 	int nid;
259 
260 	/* check allowed key size */
261 	if (strncmp(line + 16, "256", 3) == 0)
262 		flen = 32, nid = NID_X9_62_prime256v1;
263 	else if (strncmp(line + 16, "384", 3) == 0)
264 		flen = 48, nid = NID_secp384r1;
265 	else if (strncmp(line + 16, "521", 3) == 0)
266 		flen = 66, nid = NID_secp521r1;
267 	else
268 		return NULL;
269 
270 	/* find the mime-blob */
271 	b = line;
272 
273 	if (!b)
274 		return NULL;
275 
276 	/* find the first whitespace */
277 	while (*b && *b != ' ')
278 		b++;
279 
280 	/* skip that whitespace */
281 	b++;
282 
283 	/* find the end of the blob / comment */
284 	for (c = b; *c && *c != ' ' && 'c' != '\t' && *c != '\r'
285 	     && *c != '\n'; c++) ;
286 
287 	*c = 0;
288 
289 	/* decode binary data */
290 	if (sc_base64_decode(b, decoded, OPENSSH_LINE_MAX) < 0)
291 		return NULL;
292 
293 	i = 0;
294 	/* get integer from blob */
295 	len =
296 	    (decoded[i] << 24) + (decoded[i + 1] << 16) +
297 	    (decoded[i + 2] << 8) + (decoded[i + 3]);
298 	i += 4;
299 
300 	/* always check 'len' to get safe 'i' as index into 'decoded' array */
301 	if (len != 19)
302 		return NULL;
303 	/* check key type (must be same in decoded data and at line start) */
304 	if (strncmp((char *)&decoded[i], line, 19) != 0)
305 		return NULL;
306 	i += len;
307 
308 	/* get integer from blob */
309 	len =
310 	    (decoded[i] << 24) + (decoded[i + 1] << 16) +
311 	    (decoded[i + 2] << 8) + (decoded[i + 3]);
312 	i += 4;
313 
314 	/* check curve name - must match key type */
315 	if(len != 8)
316 		return NULL;
317 	if (strncmp((char *)&decoded[i], line + 11, 8) != 0)
318 		return NULL;
319 	i += len;
320 
321 	/* get integer from blob */
322 	len =
323 	    (decoded[i] << 24) + (decoded[i + 1] << 16) +
324 	    (decoded[i + 2] << 8) + (decoded[i + 3]);
325 	i += 4;
326 
327 	/* read public key (uncompressed point) */
328 	/* test if data length is corresponding to key size */
329 	if (len != 1 + flen * 2)
330 		return NULL;
331 
332 	/* check uncompressed indicator */
333 	if (decoded[i] != 4 )
334 		return NULL;
335 	i++;
336 
337 	/* create key */
338 	ec_key = EC_KEY_new_by_curve_name(nid);
339 
340 	/* read point coordinates */
341 	x = BN_bin2bn(decoded + i, flen, NULL);
342 	i += flen;
343 	y = BN_bin2bn(decoded + i, flen, NULL);
344 
345 	/* do error checking here: valid x, y, ec_key, point on curve.. */
346 	if (!EC_KEY_set_public_key_affine_coordinates(ec_key, x, y)) {
347 		EC_KEY_free(ec_key);
348 		BN_free(x);
349 		BN_free(y);
350 		return NULL;
351 	}
352 
353 	key = EVP_PKEY_new();
354 	EVP_PKEY_assign_EC_KEY(key, ec_key);
355 	return key;
356 }
357 
match_user_openssh(EVP_PKEY * authkey,const char * login)358 extern int match_user_openssh(EVP_PKEY *authkey, const char *login)
359 {
360 	char filename[PATH_MAX];
361 	char line[OPENSSH_LINE_MAX];
362 	struct passwd *pw;
363 	int found;
364 	FILE *file;
365 
366 	pw = getpwnam(login);
367 	if (!pw || !pw->pw_dir)
368 		return -1;
369 
370 	snprintf(filename, PATH_MAX, "%s/.ssh/authorized_keys", pw->pw_dir);
371 
372 	file = fopen(filename, "r");
373 	if (!file)
374 		return -1;
375 
376 	found = 0;
377 	do {
378 		EVP_PKEY *key = NULL;
379 		char *cp;
380 		if (!fgets(line, sizeof line, file))
381 			break;
382 
383 		/* Skip leading whitespace, empty and comment lines. */
384 		for (cp = line; *cp == ' ' || *cp == '\t'; cp++) {
385 		}
386 		if (!*cp || *cp == '\n' || *cp == '#') {
387 			continue;
388 		}
389 
390 		if (*cp >= '0' && *cp <= '9') {
391 			/* ssh v1 key format */
392 			key = ssh1_line_to_key(cp);
393 		} else if (strncmp("ssh-rsa", cp, 7) == 0) {
394 			/* ssh v2 rsa key format */
395 			key = ssh2_line_to_key(cp);
396 		} else if (strncmp("ecdsa-sha2-nistp", cp, 16) == 0) {
397 			/* ssh nistp256/384/521 key */
398 			key = ssh_nistp_line_to_key(cp);
399 		}
400 		if (key == NULL)
401 			continue;
402 
403 		if (1 == EVP_PKEY_cmp(authkey, key)) {
404 			found = 1;
405 		}
406 		EVP_PKEY_free(key);
407 	} while (found == 0);
408 
409 	fclose(file);
410 
411 	return found;
412 }
413