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