auth2.c revision 69591
1160814Ssimon/*
2160814Ssimon * Copyright (c) 2000 Markus Friedl.  All rights reserved.
3160814Ssimon *
4160814Ssimon * Redistribution and use in source and binary forms, with or without
5160814Ssimon * modification, are permitted provided that the following conditions
6337982Sjkim * are met:
7160814Ssimon * 1. Redistributions of source code must retain the above copyright
8160814Ssimon *    notice, this list of conditions and the following disclaimer.
9160814Ssimon * 2. Redistributions in binary form must reproduce the above copyright
10160814Ssimon *    notice, this list of conditions and the following disclaimer in the
11160814Ssimon *    documentation and/or other materials provided with the distribution.
12160814Ssimon *
13280297Sjkim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14160814Ssimon * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15160814Ssimon * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16160814Ssimon * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17160814Ssimon * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18160814Ssimon * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19160814Ssimon * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20160814Ssimon * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21160814Ssimon * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22160814Ssimon * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23160814Ssimon */
24160814Ssimon
25160814Ssimon#include "includes.h"
26160814SsimonRCSID("$OpenBSD: auth2.c,v 1.20 2000/10/14 12:16:56 markus Exp $");
27160814SsimonRCSID("$FreeBSD: head/crypto/openssh/auth2.c 69591 2000-12-05 02:55:12Z green $");
28160814Ssimon
29160814Ssimon#include <openssl/dsa.h>
30160814Ssimon#include <openssl/rsa.h>
31160814Ssimon#include <openssl/evp.h>
32160814Ssimon
33160814Ssimon#include "xmalloc.h"
34160814Ssimon#include "rsa.h"
35160814Ssimon#include "ssh.h"
36160814Ssimon#include "pty.h"
37160814Ssimon#include "packet.h"
38160814Ssimon#include "buffer.h"
39160814Ssimon#include "servconf.h"
40160814Ssimon#include "compat.h"
41160814Ssimon#include "channels.h"
42160814Ssimon#include "bufaux.h"
43160814Ssimon#include "ssh2.h"
44160814Ssimon#include "auth.h"
45160814Ssimon#include "session.h"
46160814Ssimon#include "dispatch.h"
47160814Ssimon#include "auth.h"
48160814Ssimon#include "key.h"
49160814Ssimon#include "kex.h"
50160814Ssimon
51160814Ssimon#include "dsa.h"
52160814Ssimon#include "uidswap.h"
53160814Ssimon#include "auth-options.h"
54160814Ssimon
55160814Ssimon#ifdef HAVE_LOGIN_CAP
56160814Ssimon#include <login_cap.h>
57160814Ssimon#endif /* HAVE_LOGIN_CAP */
58160814Ssimon
59160814Ssimon/* import */
60160814Ssimonextern ServerOptions options;
61280297Sjkimextern unsigned char *session_id2;
62160814Ssimonextern int session_id2_len;
63160814Ssimon
64160814Ssimonstatic Authctxt	*x_authctxt = NULL;
65160814Ssimonstatic int one = 1;
66160814Ssimon
67280297Sjkimtypedef struct Authmethod Authmethod;
68160814Ssimonstruct Authmethod {
69160814Ssimon	char	*name;
70160814Ssimon	int	(*userauth)(Authctxt *authctxt);
71160814Ssimon	int	*enabled;
72160814Ssimon};
73160814Ssimon
74160814Ssimon/* protocol */
75160814Ssimon
76160814Ssimonvoid	input_service_request(int type, int plen, void *ctxt);
77160814Ssimonvoid	input_userauth_request(int type, int plen, void *ctxt);
78160814Ssimonvoid	protocol_error(int type, int plen, void *ctxt);
79280297Sjkim
80280297Sjkim
81280297Sjkim/* helper */
82280297SjkimAuthmethod	*authmethod_lookup(const char *name);
83280297Sjkimstruct passwd	*pwcopy(struct passwd *pw);
84160814Ssimonint	user_dsa_key_allowed(struct passwd *pw, Key *key);
85160814Ssimonchar	*authmethods_get(void);
86280297Sjkim
87280297Sjkim/* auth */
88280297Sjkimint	userauth_none(Authctxt *authctxt);
89280297Sjkimint	userauth_passwd(Authctxt *authctxt);
90280297Sjkimint	userauth_pubkey(Authctxt *authctxt);
91280297Sjkimint	userauth_kbdint(Authctxt *authctxt);
92280297Sjkim
93280297SjkimAuthmethod authmethods[] = {
94280297Sjkim	{"none",
95280297Sjkim		userauth_none,
96160814Ssimon		&one},
97160814Ssimon	{"publickey",
98280297Sjkim		userauth_pubkey,
99160814Ssimon		&options.dsa_authentication},
100160814Ssimon	{"keyboard-interactive",
101160814Ssimon		userauth_kbdint,
102160814Ssimon		&options.kbd_interactive_authentication},
103160814Ssimon	{"password",
104160814Ssimon		userauth_passwd,
105160814Ssimon		&options.password_authentication},
106160814Ssimon	{NULL, NULL, NULL}
107160814Ssimon};
108160814Ssimon
109160814Ssimon/*
110280297Sjkim * loop until authctxt->success == TRUE
111160814Ssimon */
112160814Ssimon
113160814Ssimonvoid
114280297Sjkimdo_authentication2()
115280297Sjkim{
116280297Sjkim	Authctxt *authctxt = xmalloc(sizeof(*authctxt));
117280297Sjkim	memset(authctxt, 'a', sizeof(*authctxt));
118160814Ssimon	authctxt->valid = 0;
119280297Sjkim	authctxt->attempt = 0;
120280297Sjkim	authctxt->success = 0;
121280297Sjkim	x_authctxt = authctxt;		/*XXX*/
122280297Sjkim
123280297Sjkim#ifdef KRB4
124280297Sjkim	/* turn off kerberos, not supported by SSH2 */
125280297Sjkim	options.krb4_authentication = 0;
126280297Sjkim#endif
127280297Sjkim	dispatch_init(&protocol_error);
128280297Sjkim	dispatch_set(SSH2_MSG_SERVICE_REQUEST, &input_service_request);
129280297Sjkim	dispatch_run(DISPATCH_BLOCK, &authctxt->success, authctxt);
130280297Sjkim	do_authenticated2();
131160814Ssimon}
132160814Ssimon
133280297Sjkimvoid
134280297Sjkimprotocol_error(int type, int plen, void *ctxt)
135280297Sjkim{
136280297Sjkim	log("auth: protocol error: type %d plen %d", type, plen);
137280297Sjkim	packet_start(SSH2_MSG_UNIMPLEMENTED);
138280297Sjkim	packet_put_int(0);
139160814Ssimon	packet_send();
140337982Sjkim	packet_write_wait();
141160814Ssimon}
142280297Sjkim
143280297Sjkimvoid
144280297Sjkiminput_service_request(int type, int plen, void *ctxt)
145280297Sjkim{
146280297Sjkim	Authctxt *authctxt = ctxt;
147280297Sjkim	unsigned int len;
148280297Sjkim	int accept = 0;
149280297Sjkim	char *service = packet_get_string(&len);
150280297Sjkim	packet_done();
151280297Sjkim
152280297Sjkim	if (authctxt == NULL)
153280297Sjkim		fatal("input_service_request: no authctxt");
154280297Sjkim
155160814Ssimon	if (strcmp(service, "ssh-userauth") == 0) {
156160814Ssimon		if (!authctxt->success) {
157280297Sjkim			accept = 1;
158280297Sjkim			/* now we can handle user-auth requests */
159280297Sjkim			dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &input_userauth_request);
160160814Ssimon		}
161337982Sjkim	}
162337982Sjkim	/* XXX all other service requests are denied */
163337982Sjkim
164337982Sjkim	if (accept) {
165337982Sjkim		packet_start(SSH2_MSG_SERVICE_ACCEPT);
166280297Sjkim		packet_put_cstring(service);
167280297Sjkim		packet_send();
168280297Sjkim		packet_write_wait();
169280297Sjkim	} else {
170280297Sjkim		debug("bad service request %s", service);
171280297Sjkim		packet_disconnect("bad service request %s", service);
172280297Sjkim	}
173280297Sjkim	xfree(service);
174280297Sjkim}
175280297Sjkim
176280297Sjkimvoid
177280297Sjkiminput_userauth_request(int type, int plen, void *ctxt)
178280297Sjkim{
179280297Sjkim	Authctxt *authctxt = ctxt;
180280297Sjkim	Authmethod *m = NULL;
181280297Sjkim	int authenticated = 0;
182280297Sjkim	char *user, *service, *method, *authmsg = NULL;
183280297Sjkim#ifdef HAVE_LOGIN_CAP
184160814Ssimon	login_cap_t *lc;
185160814Ssimon#endif /* HAVE_LOGIN_CAP */
186160814Ssimon#if defined(HAVE_LOGIN_CAP) || defined(LOGIN_ACCESS)
187280297Sjkim	const char *from_host, *from_ip;
188280297Sjkim
189280297Sjkim	from_host = get_canonical_hostname();
190280297Sjkim	from_ip = get_remote_ipaddr();
191280297Sjkim#endif /* HAVE_LOGIN_CAP || LOGIN_ACCESS */
192280297Sjkim
193280297Sjkim	if (authctxt == NULL)
194280297Sjkim		fatal("input_userauth_request: no authctxt");
195280297Sjkim	if (authctxt->attempt++ >= AUTH_FAIL_MAX)
196160814Ssimon		packet_disconnect("too many failed userauth_requests");
197280297Sjkim
198280297Sjkim	user = packet_get_string(NULL);
199280297Sjkim	service = packet_get_string(NULL);
200280297Sjkim	method = packet_get_string(NULL);
201280297Sjkim	debug("userauth-request for user %s service %s method %s", user, service, method);
202160814Ssimon	debug("attempt #%d", authctxt->attempt);
203280297Sjkim
204280297Sjkim	if (authctxt->attempt == 1) {
205280297Sjkim		/* setup auth context */
206280297Sjkim		struct passwd *pw = NULL;
207337982Sjkim		setproctitle("%s", user);
208280297Sjkim		pw = getpwnam(user);
209280297Sjkim		if (pw && allowed_user(pw) && strcmp(service, "ssh-connection")==0) {
210280297Sjkim			authctxt->pw = pwcopy(pw);
211280297Sjkim			authctxt->valid = 1;
212280297Sjkim			debug2("input_userauth_request: setting up authctxt for %s", user);
213337982Sjkim#ifdef USE_PAM
214280297Sjkim			start_pam(pw);
215280297Sjkim#endif
216280297Sjkim		} else {
217280297Sjkim			log("input_userauth_request: illegal user %s", user);
218280297Sjkim		}
219280297Sjkim		authctxt->user = xstrdup(user);
220280297Sjkim		authctxt->service = xstrdup(service);
221280297Sjkim	} else if (authctxt->valid) {
222280297Sjkim		if (strcmp(user, authctxt->user) != 0 ||
223280297Sjkim		    strcmp(service, authctxt->service) != 0) {
224280297Sjkim			log("input_userauth_request: missmatch: (%s,%s)!=(%s,%s)",
225280297Sjkim			    user, service, authctxt->user, authctxt->service);
226280297Sjkim			authctxt->valid = 0;
227280297Sjkim		}
228280297Sjkim	}
229280297Sjkim
230280297Sjkim#ifdef HAVE_LOGIN_CAP
231280297Sjkim	if (authctxt->pw != NULL) {
232280297Sjkim		lc = login_getpwclass(authctxt->pw);
233160814Ssimon		if (lc == NULL)
234280297Sjkim			lc = login_getclassbyname(NULL, authctxt->pw);
235280297Sjkim		if (!auth_hostok(lc, from_host, from_ip)) {
236280297Sjkim			log("Denied connection for %.200s from %.200s [%.200s].",
237280297Sjkim			    authctxt->pw->pw_name, from_host, from_ip);
238280297Sjkim			packet_disconnect("Sorry, you are not allowed to connect.");
239280297Sjkim		}
240280297Sjkim		if (!auth_timeok(lc, time(NULL))) {
241280297Sjkim			log("LOGIN %.200s REFUSED (TIME) FROM %.200s",
242280297Sjkim			    authctxt->pw->pw_name, from_host);
243280297Sjkim			packet_disconnect("Logins not available right now.");
244280297Sjkim		}
245280297Sjkim		login_close(lc);
246280297Sjkim		lc = NULL;
247280297Sjkim	}
248280297Sjkim#endif  /* HAVE_LOGIN_CAP */
249280297Sjkim#ifdef LOGIN_ACCESS
250160814Ssimon	if (authctxt->pw != NULL &&
251160814Ssimon	    !login_access(authctxt->pw->pw_name, from_host)) {
252280297Sjkim		log("Denied connection for %.200s from %.200s [%.200s].",
253280297Sjkim		    authctxt->pw->pw_name, from_host, from_ip);
254160814Ssimon		packet_disconnect("Sorry, you are not allowed to connect.");
255280297Sjkim	}
256160814Ssimon#endif /* LOGIN_ACCESS */
257280297Sjkim
258280297Sjkim	m = authmethod_lookup(method);
259280297Sjkim	if (m != NULL) {
260160814Ssimon		debug2("input_userauth_request: try method %s", method);
261280297Sjkim		authenticated =	m->userauth(authctxt);
262280297Sjkim	} else {
263280297Sjkim		debug2("input_userauth_request: unsupported method %s", method);
264280297Sjkim	}
265280297Sjkim	if (!authctxt->valid && authenticated == 1) {
266280297Sjkim		log("input_userauth_request: INTERNAL ERROR: authenticated invalid user %s service %s", user, method);
267280297Sjkim		authenticated = 0;
268280297Sjkim	}
269280297Sjkim
270280297Sjkim	/* Special handling for root */
271280297Sjkim	if (authenticated == 1 &&
272280297Sjkim	    authctxt->valid && authctxt->pw->pw_uid == 0 && !options.permit_root_login) {
273280297Sjkim		authenticated = 0;
274280297Sjkim		log("ROOT LOGIN REFUSED FROM %.200s", get_canonical_hostname());
275280297Sjkim	}
276280297Sjkim
277280297Sjkim#ifdef USE_PAM
278280297Sjkim	if (authenticated && authctxt->user && !do_pam_account(authctxt->user, NULL))
279280297Sjkim		authenticated = 0;
280280297Sjkim#endif /* USE_PAM */
281280297Sjkim
282280297Sjkim	/* Log before sending the reply */
283280297Sjkim	userauth_log(authctxt, authenticated, method);
284280297Sjkim	userauth_reply(authctxt, authenticated);
285280297Sjkim
286280297Sjkim	xfree(service);
287280297Sjkim	xfree(user);
288280297Sjkim	xfree(method);
289160814Ssimon}
290160814Ssimon
291280297Sjkim
292280297Sjkimvoid
293280297Sjkimuserauth_log(Authctxt *authctxt, int authenticated, char *method)
294280297Sjkim{
295280297Sjkim	void (*authlog) (const char *fmt,...) = verbose;
296280297Sjkim	char *user = NULL, *authmsg = NULL;
297280297Sjkim
298280297Sjkim	/* Raise logging level */
299280297Sjkim	if (authenticated == 1 ||
300280297Sjkim	    !authctxt->valid ||
301280297Sjkim	    authctxt->attempt >= AUTH_FAIL_LOG ||
302280297Sjkim	    strcmp(method, "password") == 0)
303280297Sjkim		authlog = log;
304160814Ssimon
305280297Sjkim	if (authenticated == 1) {
306284283Sjkim		authmsg = "Accepted";
307284283Sjkim	} else if (authenticated == 0) {
308280297Sjkim		authmsg = "Failed";
309280297Sjkim	} else {
310280297Sjkim		authmsg = "Postponed";
311160814Ssimon	}
312280297Sjkim
313280297Sjkim	if (authctxt->valid) {
314280297Sjkim		user = authctxt->pw->pw_uid == 0 ? "ROOT" : authctxt->user;
315280297Sjkim	} else {
316280297Sjkim		user = "NOUSER";
317160814Ssimon	}
318280297Sjkim
319280297Sjkim	authlog("%s %s for %.200s from %.200s port %d ssh2",
320160814Ssimon	    authmsg,
321280297Sjkim	    method,
322160814Ssimon	    user,
323280297Sjkim	    get_remote_ipaddr(),
324280297Sjkim	    get_remote_port());
325280297Sjkim}
326280297Sjkim
327160814Ssimonvoid
328280297Sjkimuserauth_reply(Authctxt *authctxt, int authenticated)
329280297Sjkim{
330280297Sjkim	/* XXX todo: check if multiple auth methods are needed */
331280297Sjkim	if (authenticated == 1) {
332160814Ssimon		/* turn off userauth */
333280297Sjkim		dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &protocol_error);
334280297Sjkim		packet_start(SSH2_MSG_USERAUTH_SUCCESS);
335280297Sjkim		packet_send();
336237657Sjkim		packet_write_wait();
337280297Sjkim		/* now we can break out */
338280297Sjkim		authctxt->success = 1;
339280297Sjkim	} else if (authenticated == 0) {
340280297Sjkim		char *methods = authmethods_get();
341280297Sjkim		packet_start(SSH2_MSG_USERAUTH_FAILURE);
342280297Sjkim		packet_put_cstring(methods);
343280297Sjkim		packet_put_char(0);	/* XXX partial success, unused */
344280297Sjkim		packet_send();
345280297Sjkim		packet_write_wait();
346280297Sjkim		xfree(methods);
347280297Sjkim	} else {
348280297Sjkim		/* do nothing, we did already send a reply */
349280297Sjkim	}
350280297Sjkim}
351280297Sjkim
352280297Sjkimint
353280297Sjkimuserauth_none(Authctxt *authctxt)
354280297Sjkim{
355280297Sjkim	/* disable method "none", only allowed one time */
356280297Sjkim	Authmethod *m = authmethod_lookup("none");
357280297Sjkim	if (m != NULL)
358280297Sjkim		m->enabled = NULL;
359280297Sjkim	packet_done();
360280297Sjkim#ifdef USE_PAM
361280297Sjkim	return authctxt->valid ? auth_pam_password(authctxt->pw, "") : 0;
362280297Sjkim#else /* !USE_PAM */
363280297Sjkim	return authctxt->valid ? auth_password(authctxt->pw, "") : 0;
364280297Sjkim#endif /* USE_PAM */
365280297Sjkim}
366280297Sjkim
367280297Sjkimint
368280297Sjkimuserauth_passwd(Authctxt *authctxt)
369280297Sjkim{
370280297Sjkim	char *password;
371280297Sjkim	int authenticated = 0;
372280297Sjkim	int change;
373280297Sjkim	unsigned int len;
374280297Sjkim	change = packet_get_char();
375280297Sjkim	if (change)
376237657Sjkim		log("password change not supported");
377280297Sjkim	password = packet_get_string(&len);
378280297Sjkim	packet_done();
379280297Sjkim	if (authctxt->valid &&
380280297Sjkim#ifdef USE_PAM
381280297Sjkim	    auth_pam_password(authctxt->pw, password) == 1
382280297Sjkim#else
383280297Sjkim	    auth_password(authctxt->pw, password) == 1
384280297Sjkim#endif
385280297Sjkim	    )
386280297Sjkim		authenticated = 1;
387280297Sjkim	memset(password, 0, len);
388280297Sjkim	xfree(password);
389280297Sjkim	return authenticated;
390280297Sjkim}
391280297Sjkim
392280297Sjkimint
393280297Sjkimuserauth_kbdint(Authctxt *authctxt)
394280297Sjkim{
395280297Sjkim	int authenticated = 0;
396280297Sjkim	char *lang = NULL;
397280297Sjkim	char *devs = NULL;
398280297Sjkim
399280297Sjkim	lang = packet_get_string(NULL);
400280297Sjkim	devs = packet_get_string(NULL);
401280297Sjkim	packet_done();
402280297Sjkim
403280297Sjkim	debug("keyboard-interactive language %s devs %s", lang, devs);
404280297Sjkim#ifdef SKEY
405280297Sjkim	/* XXX hardcoded, we should look at devs */
406280297Sjkim	if (options.skey_authentication != 0)
407280297Sjkim		authenticated = auth2_skey(authctxt);
408280297Sjkim#endif
409280297Sjkim	xfree(lang);
410280297Sjkim	xfree(devs);
411280297Sjkim	return authenticated;
412280297Sjkim}
413280297Sjkim
414280297Sjkimint
415280297Sjkimuserauth_pubkey(Authctxt *authctxt)
416280297Sjkim{
417280297Sjkim	Buffer b;
418280297Sjkim	Key *key;
419280297Sjkim	char *pkalg, *pkblob, *sig;
420280297Sjkim	unsigned int alen, blen, slen;
421280297Sjkim	int have_sig;
422280297Sjkim	int authenticated = 0;
423280297Sjkim
424237657Sjkim	if (!authctxt->valid) {
425280297Sjkim		debug2("userauth_pubkey: disabled because of invalid user");
426280297Sjkim		return 0;
427280297Sjkim	}
428280297Sjkim	have_sig = packet_get_char();
429280297Sjkim	pkalg = packet_get_string(&alen);
430280297Sjkim	if (strcmp(pkalg, KEX_DSS) != 0) {
431280297Sjkim		log("bad pkalg %s", pkalg);	/*XXX*/
432280297Sjkim		xfree(pkalg);
433280297Sjkim		return 0;
434280297Sjkim	}
435237657Sjkim	pkblob = packet_get_string(&blen);
436280297Sjkim	key = dsa_key_from_blob(pkblob, blen);
437280297Sjkim	if (key != NULL) {
438280297Sjkim		if (have_sig) {
439280297Sjkim			sig = packet_get_string(&slen);
440280297Sjkim			packet_done();
441280297Sjkim			buffer_init(&b);
442280297Sjkim			if (datafellows & SSH_OLD_SESSIONID) {
443280297Sjkim				buffer_append(&b, session_id2, session_id2_len);
444280297Sjkim			} else {
445280297Sjkim				buffer_put_string(&b, session_id2, session_id2_len);
446280297Sjkim			}
447280297Sjkim			/* reconstruct packet */
448280297Sjkim			buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST);
449280297Sjkim			buffer_put_cstring(&b, authctxt->user);
450280297Sjkim			buffer_put_cstring(&b,
451237657Sjkim			    datafellows & SSH_BUG_PUBKEYAUTH ?
452280297Sjkim			    "ssh-userauth" :
453280297Sjkim			    authctxt->service);
454280297Sjkim			buffer_put_cstring(&b, "publickey");
455280297Sjkim			buffer_put_char(&b, have_sig);
456280297Sjkim			buffer_put_cstring(&b, KEX_DSS);
457280297Sjkim			buffer_put_string(&b, pkblob, blen);
458280297Sjkim#ifdef DEBUG_DSS
459280297Sjkim			buffer_dump(&b);
460160814Ssimon#endif
461280297Sjkim			/* test for correct signature */
462280297Sjkim			if (user_dsa_key_allowed(authctxt->pw, key) &&
463280297Sjkim			    dsa_verify(key, sig, slen, buffer_ptr(&b), buffer_len(&b)) == 1)
464280297Sjkim				authenticated = 1;
465280297Sjkim			buffer_clear(&b);
466280297Sjkim			xfree(sig);
467280297Sjkim		} else {
468280297Sjkim			debug("test whether pkalg/pkblob are acceptable");
469280297Sjkim			packet_done();
470280297Sjkim
471280297Sjkim			/* XXX fake reply and always send PK_OK ? */
472280297Sjkim			/*
473280297Sjkim			 * XXX this allows testing whether a user is allowed
474160814Ssimon			 * to login: if you happen to have a valid pubkey this
475280297Sjkim			 * message is sent. the message is NEVER sent at all
476280297Sjkim			 * if a user is not allowed to login. is this an
477280297Sjkim			 * issue? -markus
478280297Sjkim			 */
479280297Sjkim			if (user_dsa_key_allowed(authctxt->pw, key)) {
480280297Sjkim				packet_start(SSH2_MSG_USERAUTH_PK_OK);
481280297Sjkim				packet_put_string(pkalg, alen);
482280297Sjkim				packet_put_string(pkblob, blen);
483160814Ssimon				packet_send();
484280297Sjkim				packet_write_wait();
485280297Sjkim				authenticated = -1;
486280297Sjkim			}
487280297Sjkim		}
488280297Sjkim		if (authenticated != 1)
489280297Sjkim			auth_clear_options();
490280297Sjkim		key_free(key);
491280297Sjkim	}
492280297Sjkim	xfree(pkalg);
493280297Sjkim	xfree(pkblob);
494280297Sjkim	return authenticated;
495280297Sjkim}
496280297Sjkim
497280297Sjkim/* get current user */
498280297Sjkim
499280297Sjkimstruct passwd*
500280297Sjkimauth_get_user(void)
501280297Sjkim{
502280297Sjkim	return (x_authctxt != NULL && x_authctxt->valid) ? x_authctxt->pw : NULL;
503280297Sjkim}
504280297Sjkim
505280297Sjkim#define	DELIM	","
506280297Sjkim
507280297Sjkimchar *
508280297Sjkimauthmethods_get(void)
509280297Sjkim{
510280297Sjkim	Authmethod *method = NULL;
511280297Sjkim	unsigned int size = 0;
512280297Sjkim	char *list;
513280297Sjkim
514280297Sjkim	for (method = authmethods; method->name != NULL; method++) {
515280297Sjkim		if (strcmp(method->name, "none") == 0)
516280297Sjkim			continue;
517280297Sjkim		if (method->enabled != NULL && *(method->enabled) != 0) {
518160814Ssimon			if (size != 0)
519280297Sjkim				size += strlen(DELIM);
520280297Sjkim			size += strlen(method->name);
521280297Sjkim		}
522160814Ssimon	}
523280297Sjkim	size++;			/* trailing '\0' */
524160814Ssimon	list = xmalloc(size);
525280297Sjkim	list[0] = '\0';
526280297Sjkim
527280297Sjkim	for (method = authmethods; method->name != NULL; method++) {
528280297Sjkim		if (strcmp(method->name, "none") == 0)
529280297Sjkim			continue;
530280297Sjkim		if (method->enabled != NULL && *(method->enabled) != 0) {
531280297Sjkim			if (list[0] != '\0')
532280297Sjkim				strlcat(list, DELIM, size);
533280297Sjkim			strlcat(list, method->name, size);
534280297Sjkim		}
535160814Ssimon	}
536280297Sjkim	return list;
537160814Ssimon}
538280297Sjkim
539280297SjkimAuthmethod *
540280297Sjkimauthmethod_lookup(const char *name)
541280297Sjkim{
542280297Sjkim	Authmethod *method = NULL;
543280297Sjkim	if (name != NULL)
544280297Sjkim		for (method = authmethods; method->name != NULL; method++)
545280297Sjkim			if (method->enabled != NULL &&
546280297Sjkim			    *(method->enabled) != 0 &&
547280297Sjkim			    strcmp(name, method->name) == 0)
548280297Sjkim				return method;
549280297Sjkim	debug2("Unrecognized authentication method name: %s", name ? name : "NULL");
550280297Sjkim	return NULL;
551280297Sjkim}
552280297Sjkim
553280297Sjkim/* return 1 if user allows given key */
554280297Sjkimint
555280297Sjkimuser_dsa_key_allowed(struct passwd *pw, Key *key)
556280297Sjkim{
557280297Sjkim	char line[8192], file[1024];
558280297Sjkim	int found_key = 0;
559280297Sjkim	unsigned int bits = -1;
560280297Sjkim	FILE *f;
561280297Sjkim	unsigned long linenum = 0;
562280297Sjkim	struct stat st;
563160814Ssimon	Key *found;
564
565	if (pw == NULL)
566		return 0;
567
568	/* Temporarily use the user's uid. */
569	temporarily_use_uid(pw->pw_uid);
570
571	/* The authorized keys. */
572	snprintf(file, sizeof file, "%.500s/%.100s", pw->pw_dir,
573	    SSH_USER_PERMITTED_KEYS2);
574
575	/* Fail quietly if file does not exist */
576	if (stat(file, &st) < 0) {
577		/* Restore the privileged uid. */
578		restore_uid();
579		return 0;
580	}
581	/* Open the file containing the authorized keys. */
582	f = fopen(file, "r");
583	if (!f) {
584		/* Restore the privileged uid. */
585		restore_uid();
586		return 0;
587	}
588	if (options.strict_modes) {
589		int fail = 0;
590		char buf[1024];
591		/* Check open file in order to avoid open/stat races */
592		if (fstat(fileno(f), &st) < 0 ||
593		    (st.st_uid != 0 && st.st_uid != pw->pw_uid) ||
594		    (st.st_mode & 022) != 0) {
595			snprintf(buf, sizeof buf,
596			    "%s authentication refused for %.100s: "
597			    "bad ownership or modes for '%s'.",
598			    key_type(key), pw->pw_name, file);
599			fail = 1;
600		} else {
601			/* Check path to SSH_USER_PERMITTED_KEYS */
602			int i;
603			static const char *check[] = {
604				"", SSH_USER_DIR, NULL
605			};
606			for (i = 0; check[i]; i++) {
607				snprintf(line, sizeof line, "%.500s/%.100s",
608				    pw->pw_dir, check[i]);
609				if (stat(line, &st) < 0 ||
610				    (st.st_uid != 0 && st.st_uid != pw->pw_uid) ||
611				    (st.st_mode & 022) != 0) {
612					snprintf(buf, sizeof buf,
613					    "%s authentication refused for %.100s: "
614					    "bad ownership or modes for '%s'.",
615					    key_type(key), pw->pw_name, line);
616					fail = 1;
617					break;
618				}
619			}
620		}
621		if (fail) {
622			fclose(f);
623			log("%s",buf);
624			restore_uid();
625			return 0;
626		}
627	}
628	found_key = 0;
629	found = key_new(key->type);
630
631	while (fgets(line, sizeof(line), f)) {
632		char *cp, *options = NULL;
633		linenum++;
634		/* Skip leading whitespace, empty and comment lines. */
635		for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
636			;
637		if (!*cp || *cp == '\n' || *cp == '#')
638			continue;
639
640		bits = key_read(found, &cp);
641		if (bits == 0) {
642			/* no key?  check if there are options for this key */
643			int quoted = 0;
644			options = cp;
645			for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) {
646				if (*cp == '\\' && cp[1] == '"')
647					cp++;	/* Skip both */
648				else if (*cp == '"')
649					quoted = !quoted;
650			}
651			/* Skip remaining whitespace. */
652			for (; *cp == ' ' || *cp == '\t'; cp++)
653				;
654			bits = key_read(found, &cp);
655			if (bits == 0) {
656				/* still no key?  advance to next line*/
657				continue;
658			}
659		}
660		if (key_equal(found, key) &&
661		    auth_parse_options(pw, options, linenum) == 1) {
662			found_key = 1;
663			debug("matching key found: file %s, line %ld",
664			    file, linenum);
665			break;
666		}
667	}
668	restore_uid();
669	fclose(f);
670	key_free(found);
671	return found_key;
672}
673
674struct passwd *
675pwcopy(struct passwd *pw)
676{
677	struct passwd *copy = xmalloc(sizeof(*copy));
678	memset(copy, 0, sizeof(*copy));
679	copy->pw_name = xstrdup(pw->pw_name);
680	copy->pw_passwd = xstrdup(pw->pw_passwd);
681	copy->pw_uid = pw->pw_uid;
682	copy->pw_gid = pw->pw_gid;
683	copy->pw_class = xstrdup(pw->pw_class);
684	copy->pw_dir = xstrdup(pw->pw_dir);
685	copy->pw_shell = xstrdup(pw->pw_shell);
686	copy->pw_expire = pw->pw_expire;
687	copy->pw_change = pw->pw_change;
688	return copy;
689}
690