xref: /freebsd/crypto/openssh/auth2-pubkey.c (revision aa64588d28258aef88cc33b8043112e8856948d0)
1 /* $OpenBSD: auth2-pubkey.c,v 1.22 2010/03/10 23:27:17 djm Exp $ */
2 /*
3  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 
26 #include "includes.h"
27 
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 
31 #include <fcntl.h>
32 #include <pwd.h>
33 #include <stdio.h>
34 #include <stdarg.h>
35 #include <string.h>
36 #include <time.h>
37 #include <unistd.h>
38 
39 #include "xmalloc.h"
40 #include "ssh.h"
41 #include "ssh2.h"
42 #include "packet.h"
43 #include "buffer.h"
44 #include "log.h"
45 #include "servconf.h"
46 #include "compat.h"
47 #include "key.h"
48 #include "hostfile.h"
49 #include "auth.h"
50 #include "pathnames.h"
51 #include "uidswap.h"
52 #include "auth-options.h"
53 #include "canohost.h"
54 #ifdef GSSAPI
55 #include "ssh-gss.h"
56 #endif
57 #include "monitor_wrap.h"
58 #include "misc.h"
59 #include "authfile.h"
60 
61 /* import */
62 extern ServerOptions options;
63 extern u_char *session_id2;
64 extern u_int session_id2_len;
65 
66 static int
67 userauth_pubkey(Authctxt *authctxt)
68 {
69 	Buffer b;
70 	Key *key = NULL;
71 	char *pkalg;
72 	u_char *pkblob, *sig;
73 	u_int alen, blen, slen;
74 	int have_sig, pktype;
75 	int authenticated = 0;
76 
77 	if (!authctxt->valid) {
78 		debug2("userauth_pubkey: disabled because of invalid user");
79 		return 0;
80 	}
81 	have_sig = packet_get_char();
82 	if (datafellows & SSH_BUG_PKAUTH) {
83 		debug2("userauth_pubkey: SSH_BUG_PKAUTH");
84 		/* no explicit pkalg given */
85 		pkblob = packet_get_string(&blen);
86 		buffer_init(&b);
87 		buffer_append(&b, pkblob, blen);
88 		/* so we have to extract the pkalg from the pkblob */
89 		pkalg = buffer_get_string(&b, &alen);
90 		buffer_free(&b);
91 	} else {
92 		pkalg = packet_get_string(&alen);
93 		pkblob = packet_get_string(&blen);
94 	}
95 	pktype = key_type_from_name(pkalg);
96 	if (pktype == KEY_UNSPEC) {
97 		/* this is perfectly legal */
98 		logit("userauth_pubkey: unsupported public key algorithm: %s",
99 		    pkalg);
100 		goto done;
101 	}
102 	key = key_from_blob(pkblob, blen);
103 	if (key == NULL) {
104 		error("userauth_pubkey: cannot decode key: %s", pkalg);
105 		goto done;
106 	}
107 	if (key->type != pktype) {
108 		error("userauth_pubkey: type mismatch for decoded key "
109 		    "(received %d, expected %d)", key->type, pktype);
110 		goto done;
111 	}
112 	if (have_sig) {
113 		sig = packet_get_string(&slen);
114 		packet_check_eom();
115 		buffer_init(&b);
116 		if (datafellows & SSH_OLD_SESSIONID) {
117 			buffer_append(&b, session_id2, session_id2_len);
118 		} else {
119 			buffer_put_string(&b, session_id2, session_id2_len);
120 		}
121 		/* reconstruct packet */
122 		buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST);
123 		buffer_put_cstring(&b, authctxt->user);
124 		buffer_put_cstring(&b,
125 		    datafellows & SSH_BUG_PKSERVICE ?
126 		    "ssh-userauth" :
127 		    authctxt->service);
128 		if (datafellows & SSH_BUG_PKAUTH) {
129 			buffer_put_char(&b, have_sig);
130 		} else {
131 			buffer_put_cstring(&b, "publickey");
132 			buffer_put_char(&b, have_sig);
133 			buffer_put_cstring(&b, pkalg);
134 		}
135 		buffer_put_string(&b, pkblob, blen);
136 #ifdef DEBUG_PK
137 		buffer_dump(&b);
138 #endif
139 		/* test for correct signature */
140 		authenticated = 0;
141 		if (PRIVSEP(user_key_allowed(authctxt->pw, key)) &&
142 		    PRIVSEP(key_verify(key, sig, slen, buffer_ptr(&b),
143 		    buffer_len(&b))) == 1)
144 			authenticated = 1;
145 		buffer_free(&b);
146 		xfree(sig);
147 	} else {
148 		debug("test whether pkalg/pkblob are acceptable");
149 		packet_check_eom();
150 
151 		/* XXX fake reply and always send PK_OK ? */
152 		/*
153 		 * XXX this allows testing whether a user is allowed
154 		 * to login: if you happen to have a valid pubkey this
155 		 * message is sent. the message is NEVER sent at all
156 		 * if a user is not allowed to login. is this an
157 		 * issue? -markus
158 		 */
159 		if (PRIVSEP(user_key_allowed(authctxt->pw, key))) {
160 			packet_start(SSH2_MSG_USERAUTH_PK_OK);
161 			packet_put_string(pkalg, alen);
162 			packet_put_string(pkblob, blen);
163 			packet_send();
164 			packet_write_wait();
165 			authctxt->postponed = 1;
166 		}
167 	}
168 	if (authenticated != 1)
169 		auth_clear_options();
170 done:
171 	debug2("userauth_pubkey: authenticated %d pkalg %s", authenticated, pkalg);
172 	if (key != NULL)
173 		key_free(key);
174 	xfree(pkalg);
175 	xfree(pkblob);
176 	return authenticated;
177 }
178 
179 /* return 1 if user allows given key */
180 static int
181 user_key_allowed2(struct passwd *pw, Key *key, char *file)
182 {
183 	char line[SSH_MAX_PUBKEY_BYTES];
184 	const char *reason;
185 	int found_key = 0;
186 	FILE *f;
187 	u_long linenum = 0;
188 	Key *found;
189 	char *fp;
190 
191 	/* Temporarily use the user's uid. */
192 	temporarily_use_uid(pw);
193 
194 	debug("trying public key file %s", file);
195 	f = auth_openkeyfile(file, pw, options.strict_modes);
196 
197 	if (!f) {
198 		restore_uid();
199 		return 0;
200 	}
201 
202 	found_key = 0;
203 	found = key_new(key_is_cert(key) ? KEY_UNSPEC : key->type);
204 
205 	while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) {
206 		char *cp, *key_options = NULL;
207 
208 		auth_clear_options();
209 
210 		/* Skip leading whitespace, empty and comment lines. */
211 		for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
212 			;
213 		if (!*cp || *cp == '\n' || *cp == '#')
214 			continue;
215 
216 		if (key_read(found, &cp) != 1) {
217 			/* no key?  check if there are options for this key */
218 			int quoted = 0;
219 			debug2("user_key_allowed: check options: '%s'", cp);
220 			key_options = cp;
221 			for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) {
222 				if (*cp == '\\' && cp[1] == '"')
223 					cp++;	/* Skip both */
224 				else if (*cp == '"')
225 					quoted = !quoted;
226 			}
227 			/* Skip remaining whitespace. */
228 			for (; *cp == ' ' || *cp == '\t'; cp++)
229 				;
230 			if (key_read(found, &cp) != 1) {
231 				debug2("user_key_allowed: advance: '%s'", cp);
232 				/* still no key?  advance to next line*/
233 				continue;
234 			}
235 		}
236 		if (auth_parse_options(pw, key_options, file, linenum) != 1)
237 			continue;
238 		if (key->type == KEY_RSA_CERT || key->type == KEY_DSA_CERT) {
239 			if (!key_is_cert_authority)
240 				continue;
241 			if (!key_equal(found, key->cert->signature_key))
242 				continue;
243 			fp = key_fingerprint(found, SSH_FP_MD5,
244 			    SSH_FP_HEX);
245 			debug("matching CA found: file %s, line %lu, %s %s",
246 			    file, linenum, key_type(found), fp);
247 			if (key_cert_check_authority(key, 0, 0, pw->pw_name,
248 			    &reason) != 0) {
249 				xfree(fp);
250 				error("%s", reason);
251 				auth_debug_add("%s", reason);
252 				continue;
253 			}
254 			if (auth_cert_constraints(&key->cert->constraints,
255 			    pw) != 0) {
256 				xfree(fp);
257 				continue;
258 			}
259 			verbose("Accepted certificate ID \"%s\" "
260 			    "signed by %s CA %s via %s", key->cert->key_id,
261 			    key_type(found), fp, file);
262 			xfree(fp);
263 			found_key = 1;
264 			break;
265 		} else if (!key_is_cert_authority && key_equal(found, key)) {
266 			found_key = 1;
267 			debug("matching key found: file %s, line %lu",
268 			    file, linenum);
269 			fp = key_fingerprint(found, SSH_FP_MD5, SSH_FP_HEX);
270 			verbose("Found matching %s key: %s",
271 			    key_type(found), fp);
272 			xfree(fp);
273 			break;
274 		}
275 	}
276 	restore_uid();
277 	fclose(f);
278 	key_free(found);
279 	if (!found_key)
280 		debug2("key not found");
281 	return found_key;
282 }
283 
284 /* Authenticate a certificate key against TrustedUserCAKeys */
285 static int
286 user_cert_trusted_ca(struct passwd *pw, Key *key)
287 {
288 	char *ca_fp;
289 	const char *reason;
290 	int ret = 0;
291 
292 	if (!key_is_cert(key) || options.trusted_user_ca_keys == NULL)
293 		return 0;
294 
295 	ca_fp = key_fingerprint(key->cert->signature_key,
296 	    SSH_FP_MD5, SSH_FP_HEX);
297 
298 	if (key_in_file(key->cert->signature_key,
299 	    options.trusted_user_ca_keys, 1) != 1) {
300 		debug2("%s: CA %s %s is not listed in %s", __func__,
301 		    key_type(key->cert->signature_key), ca_fp,
302 		    options.trusted_user_ca_keys);
303 		goto out;
304 	}
305 	if (key_cert_check_authority(key, 0, 1, pw->pw_name, &reason) != 0) {
306 		error("%s", reason);
307 		auth_debug_add("%s", reason);
308 		goto out;
309 	}
310 	if (auth_cert_constraints(&key->cert->constraints, pw) != 0)
311 		goto out;
312 
313 	verbose("Accepted certificate ID \"%s\" signed by %s CA %s via %s",
314 	    key->cert->key_id, key_type(key->cert->signature_key), ca_fp,
315 	    options.trusted_user_ca_keys);
316 	ret = 1;
317 
318  out:
319 	if (ca_fp != NULL)
320 		xfree(ca_fp);
321 	return ret;
322 }
323 
324 /* check whether given key is in .ssh/authorized_keys* */
325 int
326 user_key_allowed(struct passwd *pw, Key *key)
327 {
328 	int success;
329 	char *file;
330 
331 	if (auth_key_is_revoked(key))
332 		return 0;
333 	if (key_is_cert(key) && auth_key_is_revoked(key->cert->signature_key))
334 		return 0;
335 
336 	success = user_cert_trusted_ca(pw, key);
337 	if (success)
338 		return success;
339 
340 	file = authorized_keys_file(pw);
341 	success = user_key_allowed2(pw, key, file);
342 	xfree(file);
343 	if (success)
344 		return success;
345 
346 	/* try suffix "2" for backward compat, too */
347 	file = authorized_keys_file2(pw);
348 	success = user_key_allowed2(pw, key, file);
349 	xfree(file);
350 	return success;
351 }
352 
353 Authmethod method_pubkey = {
354 	"publickey",
355 	userauth_pubkey,
356 	&options.pubkey_authentication
357 };
358