xref: /netbsd-src/crypto/external/bsd/openssh/dist/auth2.c (revision 79eb073490c9323108e9762431e220840e99e3b1)
1*79eb0734Srin /*	$NetBSD: auth2.c,v 1.32 2024/10/09 01:49:20 rin Exp $	*/
21c7715ddSchristos /* $OpenBSD: auth2.c,v 1.169 2024/05/17 00:30:23 djm Exp $ */
3a629fefcSchristos 
4ca32bd8dSchristos /*
5ca32bd8dSchristos  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
6ca32bd8dSchristos  *
7ca32bd8dSchristos  * Redistribution and use in source and binary forms, with or without
8ca32bd8dSchristos  * modification, are permitted provided that the following conditions
9ca32bd8dSchristos  * are met:
10ca32bd8dSchristos  * 1. Redistributions of source code must retain the above copyright
11ca32bd8dSchristos  *    notice, this list of conditions and the following disclaimer.
12ca32bd8dSchristos  * 2. Redistributions in binary form must reproduce the above copyright
13ca32bd8dSchristos  *    notice, this list of conditions and the following disclaimer in the
14ca32bd8dSchristos  *    documentation and/or other materials provided with the distribution.
15ca32bd8dSchristos  *
16ca32bd8dSchristos  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17ca32bd8dSchristos  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18ca32bd8dSchristos  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19ca32bd8dSchristos  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20ca32bd8dSchristos  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21ca32bd8dSchristos  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22ca32bd8dSchristos  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23ca32bd8dSchristos  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24ca32bd8dSchristos  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25ca32bd8dSchristos  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26ca32bd8dSchristos  */
27ca32bd8dSchristos 
28313c6c94Schristos #include "includes.h"
29*79eb0734Srin __RCSID("$NetBSD: auth2.c,v 1.32 2024/10/09 01:49:20 rin Exp $");
30cd4ada6aSchristos 
31ca32bd8dSchristos #include <sys/types.h>
32ca32bd8dSchristos #include <sys/stat.h>
33ca32bd8dSchristos #include <sys/uio.h>
34ca32bd8dSchristos 
35ca32bd8dSchristos #include <fcntl.h>
367a183406Schristos #include <limits.h>
37ca32bd8dSchristos #include <pwd.h>
38ca32bd8dSchristos #include <stdarg.h>
3947dc7704Schristos #include <string.h>
40ca32bd8dSchristos #include <unistd.h>
41aa36fcacSchristos #include <time.h>
42ca32bd8dSchristos 
43cd4ada6aSchristos #include "stdlib.h"
44ca32bd8dSchristos #include "atomicio.h"
45ca32bd8dSchristos #include "xmalloc.h"
46ca32bd8dSchristos #include "ssh2.h"
47ca32bd8dSchristos #include "packet.h"
48ca32bd8dSchristos #include "log.h"
4955a4608bSchristos #include "sshbuf.h"
508a4530f9Schristos #include "misc.h"
51ca32bd8dSchristos #include "servconf.h"
5255a4608bSchristos #include "sshkey.h"
53ca32bd8dSchristos #include "hostfile.h"
54ca32bd8dSchristos #include "auth.h"
55ca32bd8dSchristos #include "dispatch.h"
56ca32bd8dSchristos #include "pathnames.h"
57313c6c94Schristos #include "canohost.h"
5872106c52Schristos #include "pfilter.h"
59313c6c94Schristos 
60ca32bd8dSchristos #ifdef GSSAPI
61ca32bd8dSchristos #include "ssh-gss.h"
62ca32bd8dSchristos #endif
63313c6c94Schristos 
64ca32bd8dSchristos #include "monitor_wrap.h"
657a183406Schristos #include "ssherr.h"
6655a4608bSchristos #include "digest.h"
67514b5d45Schristos #include "kex.h"
68ca32bd8dSchristos 
69ca32bd8dSchristos /* import */
70ca32bd8dSchristos extern ServerOptions options;
7155a4608bSchristos extern struct sshbuf *loginmsg;
72ca32bd8dSchristos 
73ca32bd8dSchristos /* methods */
74ca32bd8dSchristos 
75ca32bd8dSchristos extern Authmethod method_none;
76ca32bd8dSchristos extern Authmethod method_pubkey;
77ca32bd8dSchristos extern Authmethod method_passwd;
78ca32bd8dSchristos extern Authmethod method_kbdint;
79ca32bd8dSchristos extern Authmethod method_hostbased;
80313c6c94Schristos #ifdef KRB5
81313c6c94Schristos extern Authmethod method_kerberos;
82313c6c94Schristos #endif
83ca32bd8dSchristos #ifdef GSSAPI
84ca32bd8dSchristos extern Authmethod method_gssapi;
85ca32bd8dSchristos #endif
86ca32bd8dSchristos 
87313c6c94Schristos static int log_flag = 0;
88313c6c94Schristos 
89ca32bd8dSchristos Authmethod *authmethods[] = {
90ca32bd8dSchristos 	&method_none,
91ca32bd8dSchristos 	&method_pubkey,
92ca32bd8dSchristos #ifdef GSSAPI
93ca32bd8dSchristos 	&method_gssapi,
94ca32bd8dSchristos #endif
95ca32bd8dSchristos 	&method_passwd,
96ca32bd8dSchristos 	&method_kbdint,
97ca32bd8dSchristos 	&method_hostbased,
98313c6c94Schristos #ifdef KRB5
99313c6c94Schristos 	&method_kerberos,
100313c6c94Schristos #endif
101ca32bd8dSchristos 	NULL
102ca32bd8dSchristos };
103ca32bd8dSchristos 
104ca32bd8dSchristos /* protocol */
105ca32bd8dSchristos 
1067a183406Schristos static int input_service_request(int, u_int32_t, struct ssh *);
1077a183406Schristos static int input_userauth_request(int, u_int32_t, struct ssh *);
108ca32bd8dSchristos 
109ca32bd8dSchristos /* helper */
110a03ec00cSchristos static Authmethod *authmethod_byname(const char *);
111ce11a51fSchristos static Authmethod *authmethod_lookup(Authctxt *, const char *);
112ce11a51fSchristos static char *authmethods_get(Authctxt *authctxt);
11300a838c4Schristos 
11400a838c4Schristos #define MATCH_NONE	0	/* method or submethod mismatch */
11500a838c4Schristos #define MATCH_METHOD	1	/* method matches (no submethod specified) */
11600a838c4Schristos #define MATCH_BOTH	2	/* method and submethod match */
11700a838c4Schristos #define MATCH_PARTIAL	3	/* method matches, submethod can't be checked */
11800a838c4Schristos static int list_starts_with(const char *, const char *, const char *);
119ca32bd8dSchristos 
120ca32bd8dSchristos char *
121ca32bd8dSchristos auth2_read_banner(void)
122ca32bd8dSchristos {
123ca32bd8dSchristos 	struct stat st;
124ca32bd8dSchristos 	char *banner = NULL;
125ca32bd8dSchristos 	size_t len, n;
126ca32bd8dSchristos 	int fd;
127ca32bd8dSchristos 
128ca32bd8dSchristos 	if ((fd = open(options.banner, O_RDONLY)) == -1)
129ca32bd8dSchristos 		return (NULL);
130ca32bd8dSchristos 	if (fstat(fd, &st) == -1) {
131ca32bd8dSchristos 		close(fd);
132ca32bd8dSchristos 		return (NULL);
133ca32bd8dSchristos 	}
134091c4109Schristos 	if (st.st_size <= 0 || st.st_size > 1*1024*1024) {
135ca32bd8dSchristos 		close(fd);
136ca32bd8dSchristos 		return (NULL);
137ca32bd8dSchristos 	}
138ca32bd8dSchristos 
139ca32bd8dSchristos 	len = (size_t)st.st_size;		/* truncate */
140ca32bd8dSchristos 	banner = xmalloc(len + 1);
141ca32bd8dSchristos 	n = atomicio(read, fd, banner, len);
142ca32bd8dSchristos 	close(fd);
143ca32bd8dSchristos 
144ca32bd8dSchristos 	if (n != len) {
14500a838c4Schristos 		free(banner);
146ca32bd8dSchristos 		return (NULL);
147ca32bd8dSchristos 	}
148ca32bd8dSchristos 	banner[n] = '\0';
149ca32bd8dSchristos 
150ca32bd8dSchristos 	return (banner);
151ca32bd8dSchristos }
152ca32bd8dSchristos 
153ce11a51fSchristos static void
154aa36fcacSchristos userauth_send_banner(struct ssh *ssh, const char *msg)
155313c6c94Schristos {
156aa36fcacSchristos 	int r;
157aa36fcacSchristos 
158aa36fcacSchristos 	if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_BANNER)) != 0 ||
159aa36fcacSchristos 	    (r = sshpkt_put_cstring(ssh, msg)) != 0 ||
160aa36fcacSchristos 	    (r = sshpkt_put_cstring(ssh, "")) != 0 ||	/* language, unused */
161aa36fcacSchristos 	    (r = sshpkt_send(ssh)) != 0)
162a244501eSkre 		fatal_fr(r, "send packet");
163313c6c94Schristos 	debug("%s: sent", __func__);
164313c6c94Schristos }
165313c6c94Schristos 
166ca32bd8dSchristos static void
167aa36fcacSchristos userauth_banner(struct ssh *ssh)
168ca32bd8dSchristos {
169ca32bd8dSchristos 	char *banner = NULL;
170ca32bd8dSchristos 
171ffae97bbSchristos 	if (options.banner == NULL)
172ca32bd8dSchristos 		return;
173ca32bd8dSchristos 
1741c7715ddSchristos 	if ((banner = mm_auth2_read_banner()) == NULL)
175ca32bd8dSchristos 		goto done;
176aa36fcacSchristos 	userauth_send_banner(ssh, banner);
177ca32bd8dSchristos 
178ca32bd8dSchristos done:
17900a838c4Schristos 	free(banner);
180ca32bd8dSchristos }
181ca32bd8dSchristos 
182ca32bd8dSchristos /*
183ca32bd8dSchristos  * loop until authctxt->success == TRUE
184ca32bd8dSchristos  */
185ca32bd8dSchristos void
186aa36fcacSchristos do_authentication2(struct ssh *ssh)
187ca32bd8dSchristos {
188aa36fcacSchristos 	Authctxt *authctxt = ssh->authctxt;
189aa36fcacSchristos 
1907a183406Schristos 	ssh_dispatch_init(ssh, &dispatch_protocol_error);
191514b5d45Schristos 	if (ssh->kex->ext_info_c)
192514b5d45Schristos 		ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, &kex_input_ext_info);
1937a183406Schristos 	ssh_dispatch_set(ssh, SSH2_MSG_SERVICE_REQUEST, &input_service_request);
1947a183406Schristos 	ssh_dispatch_run_fatal(ssh, DISPATCH_BLOCK, &authctxt->success);
1957a183406Schristos 	ssh->authctxt = NULL;
196ca32bd8dSchristos }
197ca32bd8dSchristos 
198e4d43b82Schristos static int
1997a183406Schristos input_service_request(int type, u_int32_t seq, struct ssh *ssh)
200ca32bd8dSchristos {
2017a183406Schristos 	Authctxt *authctxt = ssh->authctxt;
202aa36fcacSchristos 	char *service = NULL;
203aa36fcacSchristos 	int r, acceptit = 0;
204aa36fcacSchristos 
205aa36fcacSchristos 	if ((r = sshpkt_get_cstring(ssh, &service, NULL)) != 0 ||
206aa36fcacSchristos 	    (r = sshpkt_get_end(ssh)) != 0)
207aa36fcacSchristos 		goto out;
208ca32bd8dSchristos 
209ca32bd8dSchristos 	if (authctxt == NULL)
210ca32bd8dSchristos 		fatal("input_service_request: no authctxt");
211ca32bd8dSchristos 
212ca32bd8dSchristos 	if (strcmp(service, "ssh-userauth") == 0) {
213ca32bd8dSchristos 		if (!authctxt->success) {
214ca32bd8dSchristos 			acceptit = 1;
215ca32bd8dSchristos 			/* now we can handle user-auth requests */
216aa36fcacSchristos 			ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_REQUEST,
217aa36fcacSchristos 			    &input_userauth_request);
218ca32bd8dSchristos 		}
219ca32bd8dSchristos 	}
220ca32bd8dSchristos 	/* XXX all other service requests are denied */
221ca32bd8dSchristos 
222ca32bd8dSchristos 	if (acceptit) {
223aa36fcacSchristos 		if ((r = sshpkt_start(ssh, SSH2_MSG_SERVICE_ACCEPT)) != 0 ||
224aa36fcacSchristos 		    (r = sshpkt_put_cstring(ssh, service)) != 0 ||
225aa36fcacSchristos 		    (r = sshpkt_send(ssh)) != 0 ||
226aa36fcacSchristos 		    (r = ssh_packet_write_wait(ssh)) < 0)
227aa36fcacSchristos 			goto out;
228ca32bd8dSchristos 	} else {
229ca32bd8dSchristos 		debug("bad service request %s", service);
230aa36fcacSchristos 		ssh_packet_disconnect(ssh, "bad service request %s", service);
231ca32bd8dSchristos 	}
232514b5d45Schristos 	ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, &dispatch_protocol_error);
233aa36fcacSchristos 	r = 0;
234aa36fcacSchristos  out:
23500a838c4Schristos 	free(service);
2368db691beSchristos 	return r;
237ca32bd8dSchristos }
238ca32bd8dSchristos 
23955a4608bSchristos #define MIN_FAIL_DELAY_SECONDS 0.005
240a629fefcSchristos #define MAX_FAIL_DELAY_SECONDS 5.0
24155a4608bSchristos static double
24255a4608bSchristos user_specific_delay(const char *user)
24355a4608bSchristos {
24455a4608bSchristos 	char b[512];
24555a4608bSchristos 	size_t len = ssh_digest_bytes(SSH_DIGEST_SHA512);
24655a4608bSchristos 	u_char *hash = xmalloc(len);
24755a4608bSchristos 	double delay;
24855a4608bSchristos 
24955a4608bSchristos 	(void)snprintf(b, sizeof b, "%llu%s",
25055a4608bSchristos 	    (unsigned long long)options.timing_secret, user);
25155a4608bSchristos 	if (ssh_digest_memory(SSH_DIGEST_SHA512, b, strlen(b), hash, len) != 0)
25217418e98Schristos 		fatal_f("ssh_digest_memory");
25355a4608bSchristos 	/* 0-4.2 ms of delay */
25455a4608bSchristos 	delay = (double)PEEK_U32(hash) / 1000 / 1000 / 1000 / 1000;
25555a4608bSchristos 	freezero(hash, len);
25617418e98Schristos 	debug3_f("user specific delay %0.3lfms", delay/1000);
25755a4608bSchristos 	return MIN_FAIL_DELAY_SECONDS + delay;
25855a4608bSchristos }
25955a4608bSchristos 
26055a4608bSchristos static void
26155a4608bSchristos ensure_minimum_time_since(double start, double seconds)
26255a4608bSchristos {
26355a4608bSchristos 	struct timespec ts;
26455a4608bSchristos 	double elapsed = monotime_double() - start, req = seconds, remain;
26555a4608bSchristos 
266a629fefcSchristos 	if (elapsed > MAX_FAIL_DELAY_SECONDS) {
267a629fefcSchristos 		debug3_f("elapsed %0.3lfms exceeded the max delay "
268a629fefcSchristos 		    "requested %0.3lfms)", elapsed*1000, req*1000);
269a629fefcSchristos 		return;
270a629fefcSchristos 	}
271a629fefcSchristos 
27255a4608bSchristos 	/* if we've already passed the requested time, scale up */
27355a4608bSchristos 	while ((remain = seconds - elapsed) < 0.0)
27455a4608bSchristos 		seconds *= 2;
27555a4608bSchristos 
27655a4608bSchristos 	ts.tv_sec = remain;
27755a4608bSchristos 	ts.tv_nsec = (remain - ts.tv_sec) * 1000000000;
27817418e98Schristos 	debug3_f("elapsed %0.3lfms, delaying %0.3lfms (requested %0.3lfms)",
27917418e98Schristos 	    elapsed*1000, remain*1000, req*1000);
28055a4608bSchristos 	nanosleep(&ts, NULL);
28155a4608bSchristos }
28255a4608bSchristos 
283e4d43b82Schristos static int
2847a183406Schristos input_userauth_request(int type, u_int32_t seq, struct ssh *ssh)
285ca32bd8dSchristos {
2867a183406Schristos 	Authctxt *authctxt = ssh->authctxt;
287ca32bd8dSchristos 	Authmethod *m = NULL;
288aa36fcacSchristos 	char *user = NULL, *service = NULL, *method = NULL, *style = NULL;
289aa36fcacSchristos 	int r, authenticated = 0;
29055a4608bSchristos 	double tstart = monotime_double();
291ca32bd8dSchristos 
292ca32bd8dSchristos 	if (authctxt == NULL)
293ca32bd8dSchristos 		fatal("input_userauth_request: no authctxt");
294ca32bd8dSchristos 
295aa36fcacSchristos 	if ((r = sshpkt_get_cstring(ssh, &user, NULL)) != 0 ||
296aa36fcacSchristos 	    (r = sshpkt_get_cstring(ssh, &service, NULL)) != 0 ||
297aa36fcacSchristos 	    (r = sshpkt_get_cstring(ssh, &method, NULL)) != 0)
298aa36fcacSchristos 		goto out;
299ca32bd8dSchristos 	debug("userauth-request for user %s service %s method %s", user, service, method);
300313c6c94Schristos 	if (!log_flag) {
301313c6c94Schristos 		logit("SSH: Server;Ltype: Authname;Remote: %s-%d;Name: %s",
3025101d403Schristos 		      ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), user);
303313c6c94Schristos 		log_flag = 1;
304313c6c94Schristos 	}
305ca32bd8dSchristos 	debug("attempt %d failures %d", authctxt->attempt, authctxt->failures);
306ca32bd8dSchristos 
307ca32bd8dSchristos 	if ((style = strchr(user, ':')) != NULL)
308ca32bd8dSchristos 		*style++ = 0;
309ca32bd8dSchristos 
310a03ec00cSchristos 	if (authctxt->attempt >= 1024)
311a03ec00cSchristos 		auth_maxtries_exceeded(ssh);
312ca32bd8dSchristos 	if (authctxt->attempt++ == 0) {
313ca32bd8dSchristos 		/* setup auth context */
3141c7715ddSchristos 		authctxt->pw = mm_getpwnamallow(ssh, user);
315ca32bd8dSchristos 		if (authctxt->pw && strcmp(service, "ssh-connection")==0) {
316ca32bd8dSchristos 			authctxt->valid = 1;
31717418e98Schristos 			debug2_f("setting up authctxt for %s", user);
318ca32bd8dSchristos 		} else {
319a03ec00cSchristos 			authctxt->valid = 0;
32041768fc1Schristos 			/* Invalid user, fake password information */
321ca32bd8dSchristos 			authctxt->pw = fakepw();
322ca32bd8dSchristos 		}
323313c6c94Schristos #ifdef USE_PAM
324313c6c94Schristos 		if (options.use_pam)
3251c7715ddSchristos 			mm_start_pam(ssh);
326313c6c94Schristos #endif
32741768fc1Schristos 		ssh_packet_set_log_preamble(ssh, "%suser %s",
32841768fc1Schristos 		    authctxt->valid ? "authenticating " : "invalid ", user);
3291c7715ddSchristos 		setproctitle("%s [net]", authctxt->valid ? user : "unknown");
3301c7715ddSchristos 		authctxt->user = xstrdup(user);
331ca32bd8dSchristos 		authctxt->service = xstrdup(service);
332ca32bd8dSchristos 		authctxt->style = style ? xstrdup(style) : NULL;
333ca32bd8dSchristos 		mm_inform_authserv(service, style);
334aa36fcacSchristos 		userauth_banner(ssh);
335514b5d45Schristos 		if ((r = kex_server_update_ext_info(ssh)) != 0)
336514b5d45Schristos 			fatal_fr(r, "kex_server_update_ext_info failed");
337ce11a51fSchristos 		if (auth2_setup_methods_lists(authctxt) != 0)
338aa36fcacSchristos 			ssh_packet_disconnect(ssh,
339aa36fcacSchristos 			    "no authentication methods enabled");
340ca32bd8dSchristos 	} else if (strcmp(user, authctxt->user) != 0 ||
341ca32bd8dSchristos 	    strcmp(service, authctxt->service) != 0) {
342aa36fcacSchristos 		ssh_packet_disconnect(ssh, "Change of username or service "
343aa36fcacSchristos 		    "not allowed: (%s,%s) -> (%s,%s)",
344ca32bd8dSchristos 		    authctxt->user, authctxt->service, user, service);
345ca32bd8dSchristos 	}
346ca32bd8dSchristos 	/* reset state */
3477a183406Schristos 	auth2_challenge_stop(ssh);
348ca32bd8dSchristos 
349ca32bd8dSchristos #ifdef GSSAPI
350ca32bd8dSchristos 	/* XXX move to auth2_gssapi_stop() */
3517a183406Schristos 	ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
3527a183406Schristos 	ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL);
353ca32bd8dSchristos #endif
354ca32bd8dSchristos 
3557a183406Schristos 	auth2_authctxt_reset_info(authctxt);
356ca32bd8dSchristos 	authctxt->postponed = 0;
3576f47b660Schristos 	authctxt->server_caused_failure = 0;
358ca32bd8dSchristos 
359ca32bd8dSchristos 	/* try to authenticate user */
360ce11a51fSchristos 	m = authmethod_lookup(authctxt, method);
361ca32bd8dSchristos 	if (m != NULL && authctxt->failures < options.max_authtries) {
362ca32bd8dSchristos 		debug2("input_userauth_request: try method %s", method);
363a03ec00cSchristos 		authenticated =	m->userauth(ssh, method);
364ca32bd8dSchristos 	}
365a629fefcSchristos 	if (!authctxt->authenticated && strcmp(method, "none") != 0)
36655a4608bSchristos 		ensure_minimum_time_since(tstart,
36755a4608bSchristos 		    user_specific_delay(authctxt->user));
3687a183406Schristos 	userauth_finish(ssh, authenticated, method, NULL);
369aa36fcacSchristos 	r = 0;
370aa36fcacSchristos  out:
37100a838c4Schristos 	free(service);
37200a838c4Schristos 	free(user);
37300a838c4Schristos 	free(method);
374aa36fcacSchristos 	return r;
375ca32bd8dSchristos }
376ca32bd8dSchristos 
377ca32bd8dSchristos void
378a03ec00cSchristos userauth_finish(struct ssh *ssh, int authenticated, const char *packet_method,
379ce11a51fSchristos     const char *submethod)
380ca32bd8dSchristos {
3817a183406Schristos 	Authctxt *authctxt = ssh->authctxt;
382a03ec00cSchristos 	Authmethod *m = NULL;
383a03ec00cSchristos 	const char *method = packet_method;
384ca32bd8dSchristos 	char *methods;
385aa36fcacSchristos 	int r, partial = 0;
386ca32bd8dSchristos 
387a03ec00cSchristos 	if (authenticated) {
388a03ec00cSchristos 		if (!authctxt->valid) {
389ca32bd8dSchristos 			fatal("INTERNAL ERROR: authenticated invalid user %s",
390ca32bd8dSchristos 			    authctxt->user);
391a03ec00cSchristos 		}
392a03ec00cSchristos 		if (authctxt->postponed)
393ce11a51fSchristos 			fatal("INTERNAL ERROR: authenticated and postponed");
394a03ec00cSchristos 		/* prefer primary authmethod name to possible synonym */
395a03ec00cSchristos 		if ((m = authmethod_byname(method)) == NULL)
396a03ec00cSchristos 			fatal("INTERNAL ERROR: bad method %s", method);
3971c7715ddSchristos 		method = m->cfg->name;
398a03ec00cSchristos 	}
399ca32bd8dSchristos 
400ca32bd8dSchristos 	/* Special handling for root */
401ca32bd8dSchristos 	if (authenticated && authctxt->pw->pw_uid == 0 &&
402ffae97bbSchristos 	    !auth_root_allowed(ssh, method)) {
403ca32bd8dSchristos 		authenticated = 0;
404313c6c94Schristos #ifdef SSH_AUDIT_EVENTS
405313c6c94Schristos 		PRIVSEP(audit_event(SSH_LOGIN_ROOT_DENIED));
406313c6c94Schristos #endif
407313c6c94Schristos 	}
408313c6c94Schristos 
409313c6c94Schristos #ifdef USE_PAM
410313c6c94Schristos 	if (options.use_pam && authenticated) {
4111c7715ddSchristos 		int success = mm_do_pam_account();
4121c7715ddSchristos 
413313c6c94Schristos 		/* if PAM returned a message, send it to the user */
41455a4608bSchristos 		if (sshbuf_len(loginmsg) > 0) {
415aa36fcacSchristos 			if ((r = sshbuf_put(loginmsg, "\0", 1)) != 0)
416aa36fcacSchristos 				fatal("%s: buffer error: %s",
417aa36fcacSchristos 				    __func__, ssh_err(r));
418aa36fcacSchristos 			userauth_send_banner(ssh,
419aa36fcacSchristos 			    (const char *)sshbuf_ptr(loginmsg));
420aa36fcacSchristos 			if ((r = ssh_packet_write_wait(ssh)) < 0) {
421aa36fcacSchristos 				sshpkt_fatal(ssh, r,
422aa36fcacSchristos 				    "%s: send PAM banner", __func__);
423aa36fcacSchristos 			}
424313c6c94Schristos 		}
4251c7715ddSchristos 		if (!success) {
426313c6c94Schristos 		    fatal("Access denied for user %s by PAM account "
427313c6c94Schristos 			"configuration", authctxt->user);
428313c6c94Schristos 		}
429313c6c94Schristos 	}
430313c6c94Schristos #endif
431ca32bd8dSchristos 
432ce11a51fSchristos 	if (authenticated && options.num_auth_methods != 0) {
43300a838c4Schristos 		if (!auth2_update_methods_lists(authctxt, method, submethod)) {
434ce11a51fSchristos 			authenticated = 0;
435ce11a51fSchristos 			partial = 1;
436ce11a51fSchristos 		}
437ce11a51fSchristos 	}
438ce11a51fSchristos 
439ca32bd8dSchristos 	/* Log before sending the reply */
440aa36fcacSchristos 	auth_log(ssh, authenticated, partial, method, submethod);
441ca32bd8dSchristos 
4427a183406Schristos 	/* Update information exposed to session */
4437a183406Schristos 	if (authenticated || partial)
4447a183406Schristos 		auth2_update_session_info(authctxt, method, submethod);
4457a183406Schristos 
446ca32bd8dSchristos 	if (authctxt->postponed)
447ca32bd8dSchristos 		return;
448ca32bd8dSchristos 
449ca32bd8dSchristos 	if (authenticated == 1) {
450ca32bd8dSchristos 		/* turn off userauth */
451aa36fcacSchristos 		ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_REQUEST,
452aa36fcacSchristos 		    &dispatch_protocol_ignore);
453aa36fcacSchristos 		if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_SUCCESS)) != 0 ||
454aa36fcacSchristos 		    (r = sshpkt_send(ssh)) != 0 ||
455aa36fcacSchristos 		    (r = ssh_packet_write_wait(ssh)) < 0)
45617418e98Schristos 			fatal_fr(r, "send success packet");
457ca32bd8dSchristos 		/* now we can break out */
458ca32bd8dSchristos 		authctxt->success = 1;
45941768fc1Schristos 		ssh_packet_set_log_preamble(ssh, "user %s", authctxt->user);
460ca32bd8dSchristos 	} else {
461ca32bd8dSchristos 		/* Allow initial try of "none" auth without failure penalty */
462e4d43b82Schristos 		if (!partial && !authctxt->server_caused_failure &&
463*79eb0734Srin 		    (authctxt->attempt > 1 || strcmp(method, "none") != 0))
464ca32bd8dSchristos 			authctxt->failures++;
4658a4530f9Schristos 		if (authctxt->failures >= options.max_authtries)
466aa36fcacSchristos 			auth_maxtries_exceeded(ssh);
467ce11a51fSchristos 		methods = authmethods_get(authctxt);
46817418e98Schristos 		debug3_f("failure partial=%d next methods=\"%s\"",
469ce11a51fSchristos 		    partial, methods);
470aa36fcacSchristos 		if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_FAILURE)) != 0 ||
471aa36fcacSchristos 		    (r = sshpkt_put_cstring(ssh, methods)) != 0 ||
472aa36fcacSchristos 		    (r = sshpkt_put_u8(ssh, partial)) != 0 ||
473aa36fcacSchristos 		    (r = sshpkt_send(ssh)) != 0 ||
474aa36fcacSchristos 		    (r = ssh_packet_write_wait(ssh)) < 0)
47517418e98Schristos 			fatal_fr(r, "send failure packet");
47600a838c4Schristos 		free(methods);
477ca32bd8dSchristos 	}
478ca32bd8dSchristos }
479ca32bd8dSchristos 
480ce11a51fSchristos /*
481ce11a51fSchristos  * Checks whether method is allowed by at least one AuthenticationMethods
482ce11a51fSchristos  * methods list. Returns 1 if allowed, or no methods lists configured.
483ce11a51fSchristos  * 0 otherwise.
484ce11a51fSchristos  */
48500a838c4Schristos int
48600a838c4Schristos auth2_method_allowed(Authctxt *authctxt, const char *method,
48700a838c4Schristos     const char *submethod)
488ce11a51fSchristos {
489ce11a51fSchristos 	u_int i;
490ce11a51fSchristos 
491ce11a51fSchristos 	/*
492ce11a51fSchristos 	 * NB. authctxt->num_auth_methods might be zero as a result of
493ce11a51fSchristos 	 * auth2_setup_methods_lists(), so check the configuration.
494ce11a51fSchristos 	 */
495ce11a51fSchristos 	if (options.num_auth_methods == 0)
496ce11a51fSchristos 		return 1;
497ce11a51fSchristos 	for (i = 0; i < authctxt->num_auth_methods; i++) {
49800a838c4Schristos 		if (list_starts_with(authctxt->auth_methods[i], method,
49900a838c4Schristos 		    submethod) != MATCH_NONE)
500ce11a51fSchristos 			return 1;
501ce11a51fSchristos 	}
502ce11a51fSchristos 	return 0;
503ce11a51fSchristos }
504ce11a51fSchristos 
505ca32bd8dSchristos static char *
506ce11a51fSchristos authmethods_get(Authctxt *authctxt)
507ca32bd8dSchristos {
50855a4608bSchristos 	struct sshbuf *b;
509ca32bd8dSchristos 	char *list;
51055a4608bSchristos 	int i, r;
511ca32bd8dSchristos 
51255a4608bSchristos 	if ((b = sshbuf_new()) == NULL)
51317418e98Schristos 		fatal_f("sshbuf_new failed");
514ca32bd8dSchristos 	for (i = 0; authmethods[i] != NULL; i++) {
5151c7715ddSchristos 		if (strcmp(authmethods[i]->cfg->name, "none") == 0)
516ca32bd8dSchristos 			continue;
5171c7715ddSchristos 		if (authmethods[i]->cfg->enabled == NULL ||
5181c7715ddSchristos 		    *(authmethods[i]->cfg->enabled) == 0)
519ce11a51fSchristos 			continue;
5201c7715ddSchristos 		if (!auth2_method_allowed(authctxt, authmethods[i]->cfg->name,
52100a838c4Schristos 		    NULL))
522ce11a51fSchristos 			continue;
52355a4608bSchristos 		if ((r = sshbuf_putf(b, "%s%s", sshbuf_len(b) ? "," : "",
5241c7715ddSchristos 		    authmethods[i]->cfg->name)) != 0)
52517418e98Schristos 			fatal_fr(r, "buffer error");
526ca32bd8dSchristos 	}
52755a4608bSchristos 	if ((list = sshbuf_dup_string(b)) == NULL)
52817418e98Schristos 		fatal_f("sshbuf_dup_string failed");
52955a4608bSchristos 	sshbuf_free(b);
530ca32bd8dSchristos 	return list;
531ca32bd8dSchristos }
532ca32bd8dSchristos 
533ca32bd8dSchristos static Authmethod *
534a03ec00cSchristos authmethod_byname(const char *name)
535ca32bd8dSchristos {
536ca32bd8dSchristos 	int i;
537ca32bd8dSchristos 
538a03ec00cSchristos 	if (name == NULL)
539a03ec00cSchristos 		fatal_f("NULL authentication method name");
540a03ec00cSchristos 	for (i = 0; authmethods[i] != NULL; i++) {
5411c7715ddSchristos 		if (strcmp(name, authmethods[i]->cfg->name) == 0 ||
5421c7715ddSchristos 		    (authmethods[i]->cfg->synonym != NULL &&
5431c7715ddSchristos 		    strcmp(name, authmethods[i]->cfg->synonym) == 0))
544ca32bd8dSchristos 			return authmethods[i];
545a03ec00cSchristos 	}
546a03ec00cSchristos 	debug_f("unrecognized authentication method name: %s", name);
547ca32bd8dSchristos 	return NULL;
548ca32bd8dSchristos }
549ca32bd8dSchristos 
550a03ec00cSchristos static Authmethod *
551a03ec00cSchristos authmethod_lookup(Authctxt *authctxt, const char *name)
552a03ec00cSchristos {
553a03ec00cSchristos 	Authmethod *method;
554a03ec00cSchristos 
555a03ec00cSchristos 	if ((method = authmethod_byname(name)) == NULL)
556a03ec00cSchristos 		return NULL;
557a03ec00cSchristos 
5581c7715ddSchristos 	if (method->cfg->enabled == NULL || *(method->cfg->enabled) == 0) {
559a03ec00cSchristos 		debug3_f("method %s not enabled", name);
560a03ec00cSchristos 		return NULL;
561a03ec00cSchristos 	}
5621c7715ddSchristos 	if (!auth2_method_allowed(authctxt, method->cfg->name, NULL)) {
563a03ec00cSchristos 		debug3_f("method %s not allowed "
564a03ec00cSchristos 		    "by AuthenticationMethods", name);
565a03ec00cSchristos 		return NULL;
566a03ec00cSchristos 	}
567a03ec00cSchristos 	return method;
568a03ec00cSchristos }
569a03ec00cSchristos 
570ce11a51fSchristos /*
571ce11a51fSchristos  * Prune the AuthenticationMethods supplied in the configuration, removing
572ce11a51fSchristos  * any methods lists that include disabled methods. Note that this might
573ce11a51fSchristos  * leave authctxt->num_auth_methods == 0, even when multiple required auth
574ce11a51fSchristos  * has been requested. For this reason, all tests for whether multiple is
575ce11a51fSchristos  * enabled should consult options.num_auth_methods directly.
576ce11a51fSchristos  */
577ce11a51fSchristos int
578ce11a51fSchristos auth2_setup_methods_lists(Authctxt *authctxt)
579ce11a51fSchristos {
580ce11a51fSchristos 	u_int i;
581ce11a51fSchristos 
582aa36fcacSchristos 	/* First, normalise away the "any" pseudo-method */
583aa36fcacSchristos 	if (options.num_auth_methods == 1 &&
584aa36fcacSchristos 	    strcmp(options.auth_methods[0], "any") == 0) {
585aa36fcacSchristos 		free(options.auth_methods[0]);
586aa36fcacSchristos 		options.auth_methods[0] = NULL;
587aa36fcacSchristos 		options.num_auth_methods = 0;
588aa36fcacSchristos 	}
589aa36fcacSchristos 
590ce11a51fSchristos 	if (options.num_auth_methods == 0)
591ce11a51fSchristos 		return 0;
59217418e98Schristos 	debug3_f("checking methods");
593ce11a51fSchristos 	authctxt->auth_methods = xcalloc(options.num_auth_methods,
594ce11a51fSchristos 	    sizeof(*authctxt->auth_methods));
595ce11a51fSchristos 	authctxt->num_auth_methods = 0;
596ce11a51fSchristos 	for (i = 0; i < options.num_auth_methods; i++) {
597ce11a51fSchristos 		if (auth2_methods_valid(options.auth_methods[i], 1) != 0) {
598ce11a51fSchristos 			logit("Authentication methods list \"%s\" contains "
599ce11a51fSchristos 			    "disabled method, skipping",
600ce11a51fSchristos 			    options.auth_methods[i]);
601ce11a51fSchristos 			continue;
602ce11a51fSchristos 		}
603ce11a51fSchristos 		debug("authentication methods list %d: %s",
604ce11a51fSchristos 		    authctxt->num_auth_methods, options.auth_methods[i]);
605ce11a51fSchristos 		authctxt->auth_methods[authctxt->num_auth_methods++] =
606ce11a51fSchristos 		    xstrdup(options.auth_methods[i]);
607ce11a51fSchristos 	}
608ce11a51fSchristos 	if (authctxt->num_auth_methods == 0) {
609ce11a51fSchristos 		error("No AuthenticationMethods left after eliminating "
610ce11a51fSchristos 		    "disabled methods");
611ce11a51fSchristos 		return -1;
612ce11a51fSchristos 	}
613ce11a51fSchristos 	return 0;
614ce11a51fSchristos }
615ce11a51fSchristos 
616ce11a51fSchristos static int
61700a838c4Schristos list_starts_with(const char *methods, const char *method,
61800a838c4Schristos     const char *submethod)
619ce11a51fSchristos {
620ce11a51fSchristos 	size_t l = strlen(method);
62100a838c4Schristos 	int match;
62200a838c4Schristos 	const char *p;
623ce11a51fSchristos 
624ce11a51fSchristos 	if (strncmp(methods, method, l) != 0)
62500a838c4Schristos 		return MATCH_NONE;
62600a838c4Schristos 	p = methods + l;
62700a838c4Schristos 	match = MATCH_METHOD;
62800a838c4Schristos 	if (*p == ':') {
62900a838c4Schristos 		if (!submethod)
63000a838c4Schristos 			return MATCH_PARTIAL;
63100a838c4Schristos 		l = strlen(submethod);
63200a838c4Schristos 		p += 1;
63300a838c4Schristos 		if (strncmp(submethod, p, l))
63400a838c4Schristos 			return MATCH_NONE;
63500a838c4Schristos 		p += l;
63600a838c4Schristos 		match = MATCH_BOTH;
63700a838c4Schristos 	}
63800a838c4Schristos 	if (*p != ',' && *p != '\0')
63900a838c4Schristos 		return MATCH_NONE;
64000a838c4Schristos 	return match;
641ce11a51fSchristos }
642ce11a51fSchristos 
643ce11a51fSchristos /*
644ce11a51fSchristos  * Remove method from the start of a comma-separated list of methods.
645ce11a51fSchristos  * Returns 0 if the list of methods did not start with that method or 1
646ce11a51fSchristos  * if it did.
647ce11a51fSchristos  */
648ce11a51fSchristos static int
64900a838c4Schristos remove_method(char **methods, const char *method, const char *submethod)
650ce11a51fSchristos {
65100a838c4Schristos 	char *omethods = *methods, *p;
652ce11a51fSchristos 	size_t l = strlen(method);
65300a838c4Schristos 	int match;
654ce11a51fSchristos 
65500a838c4Schristos 	match = list_starts_with(omethods, method, submethod);
65600a838c4Schristos 	if (match != MATCH_METHOD && match != MATCH_BOTH)
657ce11a51fSchristos 		return 0;
65800a838c4Schristos 	p = omethods + l;
65900a838c4Schristos 	if (submethod && match == MATCH_BOTH)
66000a838c4Schristos 		p += 1 + strlen(submethod); /* include colon */
66100a838c4Schristos 	if (*p == ',')
66200a838c4Schristos 		p++;
66300a838c4Schristos 	*methods = xstrdup(p);
664ce11a51fSchristos 	free(omethods);
665ce11a51fSchristos 	return 1;
666ce11a51fSchristos }
667ce11a51fSchristos 
668ce11a51fSchristos /*
669ce11a51fSchristos  * Called after successful authentication. Will remove the successful method
670ce11a51fSchristos  * from the start of each list in which it occurs. If it was the last method
671ce11a51fSchristos  * in any list, then authentication is deemed successful.
672ce11a51fSchristos  * Returns 1 if the method completed any authentication list or 0 otherwise.
673ce11a51fSchristos  */
674ce11a51fSchristos int
67500a838c4Schristos auth2_update_methods_lists(Authctxt *authctxt, const char *method,
67600a838c4Schristos     const char *submethod)
677ce11a51fSchristos {
678ce11a51fSchristos 	u_int i, found = 0;
679ce11a51fSchristos 
68017418e98Schristos 	debug3_f("updating methods list after \"%s\"", method);
681ce11a51fSchristos 	for (i = 0; i < authctxt->num_auth_methods; i++) {
68200a838c4Schristos 		if (!remove_method(&(authctxt->auth_methods[i]), method,
68300a838c4Schristos 		    submethod))
684ce11a51fSchristos 			continue;
685ce11a51fSchristos 		found = 1;
686ce11a51fSchristos 		if (*authctxt->auth_methods[i] == '\0') {
687ce11a51fSchristos 			debug2("authentication methods list %d complete", i);
688ce11a51fSchristos 			return 1;
689ce11a51fSchristos 		}
690ce11a51fSchristos 		debug3("authentication methods list %d remaining: \"%s\"",
691ce11a51fSchristos 		    i, authctxt->auth_methods[i]);
692ce11a51fSchristos 	}
693ce11a51fSchristos 	/* This should not happen, but would be bad if it did */
694ce11a51fSchristos 	if (!found)
69517418e98Schristos 		fatal_f("method not in AuthenticationMethods");
696ce11a51fSchristos 	return 0;
697ce11a51fSchristos }
698ce11a51fSchristos 
6997a183406Schristos /* Reset method-specific information */
7007a183406Schristos void auth2_authctxt_reset_info(Authctxt *authctxt)
7017a183406Schristos {
7027a183406Schristos 	sshkey_free(authctxt->auth_method_key);
7037a183406Schristos 	free(authctxt->auth_method_info);
7047a183406Schristos 	authctxt->auth_method_key = NULL;
7057a183406Schristos 	authctxt->auth_method_info = NULL;
7067a183406Schristos }
7077a183406Schristos 
7087a183406Schristos /* Record auth method-specific information for logs */
7097a183406Schristos void
7107a183406Schristos auth2_record_info(Authctxt *authctxt, const char *fmt, ...)
7117a183406Schristos {
7127a183406Schristos 	va_list ap;
7137a183406Schristos 	int i;
7147a183406Schristos 
7157a183406Schristos 	free(authctxt->auth_method_info);
7167a183406Schristos 	authctxt->auth_method_info = NULL;
7177a183406Schristos 
7187a183406Schristos 	va_start(ap, fmt);
7197a183406Schristos 	i = vasprintf(&authctxt->auth_method_info, fmt, ap);
7207a183406Schristos 	va_end(ap);
7217a183406Schristos 
722cd4ada6aSchristos 	if (i == -1)
72317418e98Schristos 		fatal_f("vasprintf failed");
7247a183406Schristos }
7257a183406Schristos 
7267a183406Schristos /*
7277a183406Schristos  * Records a public key used in authentication. This is used for logging
7287a183406Schristos  * and to ensure that the same key is not subsequently accepted again for
7297a183406Schristos  * multiple authentication.
7307a183406Schristos  */
7317a183406Schristos void
7327a183406Schristos auth2_record_key(Authctxt *authctxt, int authenticated,
7337a183406Schristos     const struct sshkey *key)
7347a183406Schristos {
7357a183406Schristos 	struct sshkey **tmp, *dup;
7367a183406Schristos 	int r;
7377a183406Schristos 
738aa36fcacSchristos 	if ((r = sshkey_from_private(key, &dup)) != 0)
73917418e98Schristos 		fatal_fr(r, "copy key");
7407a183406Schristos 	sshkey_free(authctxt->auth_method_key);
7417a183406Schristos 	authctxt->auth_method_key = dup;
7427a183406Schristos 
7437a183406Schristos 	if (!authenticated)
7447a183406Schristos 		return;
7457a183406Schristos 
7467a183406Schristos 	/* If authenticated, make sure we don't accept this key again */
747aa36fcacSchristos 	if ((r = sshkey_from_private(key, &dup)) != 0)
74817418e98Schristos 		fatal_fr(r, "copy key");
7497a183406Schristos 	if (authctxt->nprev_keys >= INT_MAX ||
7507a183406Schristos 	    (tmp = recallocarray(authctxt->prev_keys, authctxt->nprev_keys,
7517a183406Schristos 	    authctxt->nprev_keys + 1, sizeof(*authctxt->prev_keys))) == NULL)
75217418e98Schristos 		fatal_f("reallocarray failed");
7537a183406Schristos 	authctxt->prev_keys = tmp;
7547a183406Schristos 	authctxt->prev_keys[authctxt->nprev_keys] = dup;
7557a183406Schristos 	authctxt->nprev_keys++;
7567a183406Schristos 
7577a183406Schristos }
7587a183406Schristos 
7597a183406Schristos /* Checks whether a key has already been previously used for authentication */
7607a183406Schristos int
7617a183406Schristos auth2_key_already_used(Authctxt *authctxt, const struct sshkey *key)
7627a183406Schristos {
7637a183406Schristos 	u_int i;
7647a183406Schristos 	char *fp;
7657a183406Schristos 
7667a183406Schristos 	for (i = 0; i < authctxt->nprev_keys; i++) {
7677a183406Schristos 		if (sshkey_equal_public(key, authctxt->prev_keys[i])) {
7687a183406Schristos 			fp = sshkey_fingerprint(authctxt->prev_keys[i],
7697a183406Schristos 			    options.fingerprint_hash, SSH_FP_DEFAULT);
77017418e98Schristos 			debug3_f("key already used: %s %s",
7717a183406Schristos 			    sshkey_type(authctxt->prev_keys[i]),
7727a183406Schristos 			    fp == NULL ? "UNKNOWN" : fp);
7737a183406Schristos 			free(fp);
7747a183406Schristos 			return 1;
7757a183406Schristos 		}
7767a183406Schristos 	}
7777a183406Schristos 	return 0;
7787a183406Schristos }
7797a183406Schristos 
7807a183406Schristos /*
7817a183406Schristos  * Updates authctxt->session_info with details of authentication. Should be
7827a183406Schristos  * whenever an authentication method succeeds.
7837a183406Schristos  */
7847a183406Schristos void
7857a183406Schristos auth2_update_session_info(Authctxt *authctxt, const char *method,
7867a183406Schristos     const char *submethod)
7877a183406Schristos {
7887a183406Schristos 	int r;
7897a183406Schristos 
7907a183406Schristos 	if (authctxt->session_info == NULL) {
7917a183406Schristos 		if ((authctxt->session_info = sshbuf_new()) == NULL)
79217418e98Schristos 			fatal_f("sshbuf_new");
7937a183406Schristos 	}
7947a183406Schristos 
7957a183406Schristos 	/* Append method[/submethod] */
7967a183406Schristos 	if ((r = sshbuf_putf(authctxt->session_info, "%s%s%s",
7977a183406Schristos 	    method, submethod == NULL ? "" : "/",
7987a183406Schristos 	    submethod == NULL ? "" : submethod)) != 0)
79917418e98Schristos 		fatal_fr(r, "append method");
8007a183406Schristos 
8017a183406Schristos 	/* Append key if present */
8027a183406Schristos 	if (authctxt->auth_method_key != NULL) {
8037a183406Schristos 		if ((r = sshbuf_put_u8(authctxt->session_info, ' ')) != 0 ||
8047a183406Schristos 		    (r = sshkey_format_text(authctxt->auth_method_key,
8057a183406Schristos 		    authctxt->session_info)) != 0)
80617418e98Schristos 			fatal_fr(r, "append key");
8077a183406Schristos 	}
8087a183406Schristos 
8097a183406Schristos 	if (authctxt->auth_method_info != NULL) {
8107a183406Schristos 		/* Ensure no ambiguity here */
8117a183406Schristos 		if (strchr(authctxt->auth_method_info, '\n') != NULL)
81217418e98Schristos 			fatal_f("auth_method_info contains \\n");
8137a183406Schristos 		if ((r = sshbuf_put_u8(authctxt->session_info, ' ')) != 0 ||
8147a183406Schristos 		    (r = sshbuf_putf(authctxt->session_info, "%s",
8157a183406Schristos 		    authctxt->auth_method_info)) != 0) {
81617418e98Schristos 			fatal_fr(r, "append method info");
8177a183406Schristos 		}
8187a183406Schristos 	}
8197a183406Schristos 	if ((r = sshbuf_put_u8(authctxt->session_info, '\n')) != 0)
82017418e98Schristos 		fatal_fr(r, "append");
8217a183406Schristos }
822ce11a51fSchristos 
823