1 /* $OpenBSD: auth2.c,v 1.164 2022/02/23 11:18:13 djm Exp $ */
3 * Copyright (c) 2000 Markus Friedl. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
28 #include <sys/types.h>
54 #include "pathnames.h"
59 #include "monitor_wrap.h"
63 extern ServerOptions options
;
64 extern struct sshbuf
*loginmsg
;
68 extern Authmethod method_none
;
69 extern Authmethod method_pubkey
;
70 extern Authmethod method_passwd
;
71 extern Authmethod method_kbdint
;
72 extern Authmethod method_hostbased
;
74 extern Authmethod method_gssapi
;
77 Authmethod
*authmethods
[] = {
91 static int input_service_request(int, u_int32_t
, struct ssh
*);
92 static int input_userauth_request(int, u_int32_t
, struct ssh
*);
95 static Authmethod
*authmethod_byname(const char *);
96 static Authmethod
*authmethod_lookup(Authctxt
*, const char *);
97 static char *authmethods_get(Authctxt
*authctxt
);
99 #define MATCH_NONE 0 /* method or submethod mismatch */
100 #define MATCH_METHOD 1 /* method matches (no submethod specified) */
101 #define MATCH_BOTH 2 /* method and submethod match */
102 #define MATCH_PARTIAL 3 /* method matches, submethod can't be checked */
103 static int list_starts_with(const char *, const char *, const char *);
106 auth2_read_banner(void)
113 if ((fd
= open(options
.banner
, O_RDONLY
)) == -1)
115 if (fstat(fd
, &st
) == -1) {
119 if (st
.st_size
<= 0 || st
.st_size
> 1*1024*1024) {
124 len
= (size_t)st
.st_size
; /* truncate */
125 banner
= xmalloc(len
+ 1);
126 n
= atomicio(read
, fd
, banner
, len
);
139 userauth_send_banner(struct ssh
*ssh
, const char *msg
)
143 if ((r
= sshpkt_start(ssh
, SSH2_MSG_USERAUTH_BANNER
)) != 0 ||
144 (r
= sshpkt_put_cstring(ssh
, msg
)) != 0 ||
145 (r
= sshpkt_put_cstring(ssh
, "")) != 0 || /* language, unused */
146 (r
= sshpkt_send(ssh
)) != 0)
147 fatal_fr(r
, "send packet");
148 debug("%s: sent", __func__
);
152 userauth_banner(struct ssh
*ssh
)
156 if (options
.banner
== NULL
)
159 if ((banner
= PRIVSEP(auth2_read_banner())) == NULL
)
161 userauth_send_banner(ssh
, banner
);
168 * loop until authctxt->success == TRUE
171 do_authentication2(struct ssh
*ssh
)
173 Authctxt
*authctxt
= ssh
->authctxt
;
175 ssh_dispatch_init(ssh
, &dispatch_protocol_error
);
176 ssh_dispatch_set(ssh
, SSH2_MSG_SERVICE_REQUEST
, &input_service_request
);
177 ssh_dispatch_run_fatal(ssh
, DISPATCH_BLOCK
, &authctxt
->success
);
178 ssh
->authctxt
= NULL
;
183 input_service_request(int type
, u_int32_t seq
, struct ssh
*ssh
)
185 Authctxt
*authctxt
= ssh
->authctxt
;
186 char *service
= NULL
;
189 if ((r
= sshpkt_get_cstring(ssh
, &service
, NULL
)) != 0 ||
190 (r
= sshpkt_get_end(ssh
)) != 0)
193 if (authctxt
== NULL
)
194 fatal("input_service_request: no authctxt");
196 if (strcmp(service
, "ssh-userauth") == 0) {
197 if (!authctxt
->success
) {
199 /* now we can handle user-auth requests */
200 ssh_dispatch_set(ssh
, SSH2_MSG_USERAUTH_REQUEST
,
201 &input_userauth_request
);
204 /* XXX all other service requests are denied */
207 if ((r
= sshpkt_start(ssh
, SSH2_MSG_SERVICE_ACCEPT
)) != 0 ||
208 (r
= sshpkt_put_cstring(ssh
, service
)) != 0 ||
209 (r
= sshpkt_send(ssh
)) != 0 ||
210 (r
= ssh_packet_write_wait(ssh
)) != 0)
213 debug("bad service request %s", service
);
214 ssh_packet_disconnect(ssh
, "bad service request %s", service
);
222 #define MIN_FAIL_DELAY_SECONDS 0.005
224 user_specific_delay(const char *user
)
227 size_t len
= ssh_digest_bytes(SSH_DIGEST_SHA512
);
228 u_char
*hash
= xmalloc(len
);
231 (void)snprintf(b
, sizeof b
, "%llu%s",
232 (unsigned long long)options
.timing_secret
, user
);
233 if (ssh_digest_memory(SSH_DIGEST_SHA512
, b
, strlen(b
), hash
, len
) != 0)
234 fatal_f("ssh_digest_memory");
235 /* 0-4.2 ms of delay */
236 delay
= (double)PEEK_U32(hash
) / 1000 / 1000 / 1000 / 1000;
238 debug3_f("user specific delay %0.3lfms", delay
/1000);
239 return MIN_FAIL_DELAY_SECONDS
+ delay
;
243 ensure_minimum_time_since(double start
, double seconds
)
246 double elapsed
= monotime_double() - start
, req
= seconds
, remain
;
248 /* if we've already passed the requested time, scale up */
249 while ((remain
= seconds
- elapsed
) < 0.0)
253 ts
.tv_nsec
= (remain
- ts
.tv_sec
) * 1000000000;
254 debug3_f("elapsed %0.3lfms, delaying %0.3lfms (requested %0.3lfms)",
255 elapsed
*1000, remain
*1000, req
*1000);
256 nanosleep(&ts
, NULL
);
261 input_userauth_request(int type
, u_int32_t seq
, struct ssh
*ssh
)
263 Authctxt
*authctxt
= ssh
->authctxt
;
264 Authmethod
*m
= NULL
;
265 char *user
= NULL
, *service
= NULL
, *method
= NULL
, *style
= NULL
;
266 int r
, authenticated
= 0;
267 double tstart
= monotime_double();
269 if (authctxt
== NULL
)
270 fatal("input_userauth_request: no authctxt");
272 if ((r
= sshpkt_get_cstring(ssh
, &user
, NULL
)) != 0 ||
273 (r
= sshpkt_get_cstring(ssh
, &service
, NULL
)) != 0 ||
274 (r
= sshpkt_get_cstring(ssh
, &method
, NULL
)) != 0)
276 debug("userauth-request for user %s service %s method %s", user
, service
, method
);
277 debug("attempt %d failures %d", authctxt
->attempt
, authctxt
->failures
);
279 if ((style
= strchr(user
, ':')) != NULL
)
282 if (authctxt
->attempt
>= 1024)
283 auth_maxtries_exceeded(ssh
);
284 if (authctxt
->attempt
++ == 0) {
285 /* setup auth context */
286 authctxt
->pw
= PRIVSEP(getpwnamallow(ssh
, user
));
287 authctxt
->user
= xstrdup(user
);
288 if (authctxt
->pw
&& strcmp(service
, "ssh-connection")==0) {
290 debug2_f("setting up authctxt for %s", user
);
293 /* Invalid user, fake password information */
294 authctxt
->pw
= fakepw();
295 #ifdef SSH_AUDIT_EVENTS
296 PRIVSEP(audit_event(ssh
, SSH_INVALID_USER
));
301 PRIVSEP(start_pam(ssh
));
303 ssh_packet_set_log_preamble(ssh
, "%suser %s",
304 authctxt
->valid
? "authenticating " : "invalid ", user
);
305 setproctitle("%s%s", authctxt
->valid
? user
: "unknown",
306 use_privsep
? " [net]" : "");
307 authctxt
->service
= xstrdup(service
);
308 authctxt
->style
= style
? xstrdup(style
) : NULL
;
310 mm_inform_authserv(service
, style
);
311 userauth_banner(ssh
);
312 if (auth2_setup_methods_lists(authctxt
) != 0)
313 ssh_packet_disconnect(ssh
,
314 "no authentication methods enabled");
315 } else if (strcmp(user
, authctxt
->user
) != 0 ||
316 strcmp(service
, authctxt
->service
) != 0) {
317 ssh_packet_disconnect(ssh
, "Change of username or service "
318 "not allowed: (%s,%s) -> (%s,%s)",
319 authctxt
->user
, authctxt
->service
, user
, service
);
322 auth2_challenge_stop(ssh
);
325 /* XXX move to auth2_gssapi_stop() */
326 ssh_dispatch_set(ssh
, SSH2_MSG_USERAUTH_GSSAPI_TOKEN
, NULL
);
327 ssh_dispatch_set(ssh
, SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE
, NULL
);
330 auth2_authctxt_reset_info(authctxt
);
331 authctxt
->postponed
= 0;
332 authctxt
->server_caused_failure
= 0;
334 /* try to authenticate user */
335 m
= authmethod_lookup(authctxt
, method
);
336 if (m
!= NULL
&& authctxt
->failures
< options
.max_authtries
) {
337 debug2("input_userauth_request: try method %s", method
);
338 authenticated
= m
->userauth(ssh
, method
);
340 if (!authctxt
->authenticated
)
341 ensure_minimum_time_since(tstart
,
342 user_specific_delay(authctxt
->user
));
343 userauth_finish(ssh
, authenticated
, method
, NULL
);
353 userauth_finish(struct ssh
*ssh
, int authenticated
, const char *packet_method
,
354 const char *submethod
)
356 Authctxt
*authctxt
= ssh
->authctxt
;
357 Authmethod
*m
= NULL
;
358 const char *method
= packet_method
;
363 if (!authctxt
->valid
) {
364 fatal("INTERNAL ERROR: authenticated invalid user %s",
367 if (authctxt
->postponed
)
368 fatal("INTERNAL ERROR: authenticated and postponed");
369 /* prefer primary authmethod name to possible synonym */
370 if ((m
= authmethod_byname(method
)) == NULL
)
371 fatal("INTERNAL ERROR: bad method %s", method
);
375 /* Special handling for root */
376 if (authenticated
&& authctxt
->pw
->pw_uid
== 0 &&
377 !auth_root_allowed(ssh
, method
)) {
379 #ifdef SSH_AUDIT_EVENTS
380 PRIVSEP(audit_event(ssh
, SSH_LOGIN_ROOT_DENIED
));
384 if (authenticated
&& options
.num_auth_methods
!= 0) {
385 if (!auth2_update_methods_lists(authctxt
, method
, submethod
)) {
391 /* Log before sending the reply */
392 auth_log(ssh
, authenticated
, partial
, method
, submethod
);
394 /* Update information exposed to session */
395 if (authenticated
|| partial
)
396 auth2_update_session_info(authctxt
, method
, submethod
);
398 if (authctxt
->postponed
)
402 if (options
.use_pam
&& authenticated
) {
403 int r
, success
= PRIVSEP(do_pam_account());
405 /* If PAM returned a message, send it to the user. */
406 if (sshbuf_len(loginmsg
) > 0) {
407 if ((r
= sshbuf_put(loginmsg
, "\0", 1)) != 0)
408 fatal("%s: buffer error: %s",
409 __func__
, ssh_err(r
));
410 userauth_send_banner(ssh
, sshbuf_ptr(loginmsg
));
411 if ((r
= ssh_packet_write_wait(ssh
)) != 0) {
413 "%s: send PAM banner", __func__
);
417 fatal("Access denied for user %s by PAM account "
418 "configuration", authctxt
->user
);
423 if (authenticated
== 1) {
424 /* turn off userauth */
425 ssh_dispatch_set(ssh
, SSH2_MSG_USERAUTH_REQUEST
,
426 &dispatch_protocol_ignore
);
427 if ((r
= sshpkt_start(ssh
, SSH2_MSG_USERAUTH_SUCCESS
)) != 0 ||
428 (r
= sshpkt_send(ssh
)) != 0 ||
429 (r
= ssh_packet_write_wait(ssh
)) != 0)
430 fatal_fr(r
, "send success packet");
431 /* now we can break out */
432 authctxt
->success
= 1;
433 ssh_packet_set_log_preamble(ssh
, "user %s", authctxt
->user
);
435 /* Allow initial try of "none" auth without failure penalty */
436 if (!partial
&& !authctxt
->server_caused_failure
&&
437 (authctxt
->attempt
> 1 || strcmp(method
, "none") != 0))
438 authctxt
->failures
++;
439 if (authctxt
->failures
>= options
.max_authtries
) {
440 #ifdef SSH_AUDIT_EVENTS
441 PRIVSEP(audit_event(ssh
, SSH_LOGIN_EXCEED_MAXTRIES
));
443 auth_maxtries_exceeded(ssh
);
445 methods
= authmethods_get(authctxt
);
446 debug3_f("failure partial=%d next methods=\"%s\"",
448 if ((r
= sshpkt_start(ssh
, SSH2_MSG_USERAUTH_FAILURE
)) != 0 ||
449 (r
= sshpkt_put_cstring(ssh
, methods
)) != 0 ||
450 (r
= sshpkt_put_u8(ssh
, partial
)) != 0 ||
451 (r
= sshpkt_send(ssh
)) != 0 ||
452 (r
= ssh_packet_write_wait(ssh
)) != 0)
453 fatal_fr(r
, "send failure packet");
459 * Checks whether method is allowed by at least one AuthenticationMethods
460 * methods list. Returns 1 if allowed, or no methods lists configured.
464 auth2_method_allowed(Authctxt
*authctxt
, const char *method
,
465 const char *submethod
)
470 * NB. authctxt->num_auth_methods might be zero as a result of
471 * auth2_setup_methods_lists(), so check the configuration.
473 if (options
.num_auth_methods
== 0)
475 for (i
= 0; i
< authctxt
->num_auth_methods
; i
++) {
476 if (list_starts_with(authctxt
->auth_methods
[i
], method
,
477 submethod
) != MATCH_NONE
)
484 authmethods_get(Authctxt
*authctxt
)
490 if ((b
= sshbuf_new()) == NULL
)
491 fatal_f("sshbuf_new failed");
492 for (i
= 0; authmethods
[i
] != NULL
; i
++) {
493 if (strcmp(authmethods
[i
]->name
, "none") == 0)
495 if (authmethods
[i
]->enabled
== NULL
||
496 *(authmethods
[i
]->enabled
) == 0)
498 if (!auth2_method_allowed(authctxt
, authmethods
[i
]->name
,
501 if ((r
= sshbuf_putf(b
, "%s%s", sshbuf_len(b
) ? "," : "",
502 authmethods
[i
]->name
)) != 0)
503 fatal_fr(r
, "buffer error");
505 if ((list
= sshbuf_dup_string(b
)) == NULL
)
506 fatal_f("sshbuf_dup_string failed");
512 authmethod_byname(const char *name
)
517 fatal_f("NULL authentication method name");
518 for (i
= 0; authmethods
[i
] != NULL
; i
++) {
519 if (strcmp(name
, authmethods
[i
]->name
) == 0 ||
520 (authmethods
[i
]->synonym
!= NULL
&&
521 strcmp(name
, authmethods
[i
]->synonym
) == 0))
522 return authmethods
[i
];
524 debug_f("unrecognized authentication method name: %s", name
);
529 authmethod_lookup(Authctxt
*authctxt
, const char *name
)
533 if ((method
= authmethod_byname(name
)) == NULL
)
536 if (method
->enabled
== NULL
|| *(method
->enabled
) == 0) {
537 debug3_f("method %s not enabled", name
);
540 if (!auth2_method_allowed(authctxt
, method
->name
, NULL
)) {
541 debug3_f("method %s not allowed "
542 "by AuthenticationMethods", name
);
549 * Check a comma-separated list of methods for validity. Is need_enable is
550 * non-zero, then also require that the methods are enabled.
551 * Returns 0 on success or -1 if the methods list is invalid.
554 auth2_methods_valid(const char *_methods
, int need_enable
)
556 char *methods
, *omethods
, *method
, *p
;
560 if (*_methods
== '\0') {
561 error("empty authentication method list");
564 omethods
= methods
= xstrdup(_methods
);
565 while ((method
= strsep(&methods
, ",")) != NULL
) {
566 for (found
= i
= 0; !found
&& authmethods
[i
] != NULL
; i
++) {
567 if ((p
= strchr(method
, ':')) != NULL
)
569 if (strcmp(method
, authmethods
[i
]->name
) != 0)
572 if (authmethods
[i
]->enabled
== NULL
||
573 *(authmethods
[i
]->enabled
) == 0) {
574 error("Disabled method \"%s\" in "
575 "AuthenticationMethods list \"%s\"",
584 error("Unknown authentication method \"%s\" in list",
596 * Prune the AuthenticationMethods supplied in the configuration, removing
597 * any methods lists that include disabled methods. Note that this might
598 * leave authctxt->num_auth_methods == 0, even when multiple required auth
599 * has been requested. For this reason, all tests for whether multiple is
600 * enabled should consult options.num_auth_methods directly.
603 auth2_setup_methods_lists(Authctxt
*authctxt
)
607 /* First, normalise away the "any" pseudo-method */
608 if (options
.num_auth_methods
== 1 &&
609 strcmp(options
.auth_methods
[0], "any") == 0) {
610 free(options
.auth_methods
[0]);
611 options
.auth_methods
[0] = NULL
;
612 options
.num_auth_methods
= 0;
615 if (options
.num_auth_methods
== 0)
617 debug3_f("checking methods");
618 authctxt
->auth_methods
= xcalloc(options
.num_auth_methods
,
619 sizeof(*authctxt
->auth_methods
));
620 authctxt
->num_auth_methods
= 0;
621 for (i
= 0; i
< options
.num_auth_methods
; i
++) {
622 if (auth2_methods_valid(options
.auth_methods
[i
], 1) != 0) {
623 logit("Authentication methods list \"%s\" contains "
624 "disabled method, skipping",
625 options
.auth_methods
[i
]);
628 debug("authentication methods list %d: %s",
629 authctxt
->num_auth_methods
, options
.auth_methods
[i
]);
630 authctxt
->auth_methods
[authctxt
->num_auth_methods
++] =
631 xstrdup(options
.auth_methods
[i
]);
633 if (authctxt
->num_auth_methods
== 0) {
634 error("No AuthenticationMethods left after eliminating "
642 list_starts_with(const char *methods
, const char *method
,
643 const char *submethod
)
645 size_t l
= strlen(method
);
649 if (strncmp(methods
, method
, l
) != 0)
652 match
= MATCH_METHOD
;
655 return MATCH_PARTIAL
;
656 l
= strlen(submethod
);
658 if (strncmp(submethod
, p
, l
))
663 if (*p
!= ',' && *p
!= '\0')
669 * Remove method from the start of a comma-separated list of methods.
670 * Returns 0 if the list of methods did not start with that method or 1
674 remove_method(char **methods
, const char *method
, const char *submethod
)
676 char *omethods
= *methods
, *p
;
677 size_t l
= strlen(method
);
680 match
= list_starts_with(omethods
, method
, submethod
);
681 if (match
!= MATCH_METHOD
&& match
!= MATCH_BOTH
)
684 if (submethod
&& match
== MATCH_BOTH
)
685 p
+= 1 + strlen(submethod
); /* include colon */
688 *methods
= xstrdup(p
);
694 * Called after successful authentication. Will remove the successful method
695 * from the start of each list in which it occurs. If it was the last method
696 * in any list, then authentication is deemed successful.
697 * Returns 1 if the method completed any authentication list or 0 otherwise.
700 auth2_update_methods_lists(Authctxt
*authctxt
, const char *method
,
701 const char *submethod
)
705 debug3_f("updating methods list after \"%s\"", method
);
706 for (i
= 0; i
< authctxt
->num_auth_methods
; i
++) {
707 if (!remove_method(&(authctxt
->auth_methods
[i
]), method
,
711 if (*authctxt
->auth_methods
[i
] == '\0') {
712 debug2("authentication methods list %d complete", i
);
715 debug3("authentication methods list %d remaining: \"%s\"",
716 i
, authctxt
->auth_methods
[i
]);
718 /* This should not happen, but would be bad if it did */
720 fatal_f("method not in AuthenticationMethods");
724 /* Reset method-specific information */
725 void auth2_authctxt_reset_info(Authctxt
*authctxt
)
727 sshkey_free(authctxt
->auth_method_key
);
728 free(authctxt
->auth_method_info
);
729 authctxt
->auth_method_key
= NULL
;
730 authctxt
->auth_method_info
= NULL
;
733 /* Record auth method-specific information for logs */
735 auth2_record_info(Authctxt
*authctxt
, const char *fmt
, ...)
740 free(authctxt
->auth_method_info
);
741 authctxt
->auth_method_info
= NULL
;
744 i
= vasprintf(&authctxt
->auth_method_info
, fmt
, ap
);
748 fatal_f("vasprintf failed");
752 * Records a public key used in authentication. This is used for logging
753 * and to ensure that the same key is not subsequently accepted again for
754 * multiple authentication.
757 auth2_record_key(Authctxt
*authctxt
, int authenticated
,
758 const struct sshkey
*key
)
760 struct sshkey
**tmp
, *dup
;
763 if ((r
= sshkey_from_private(key
, &dup
)) != 0)
764 fatal_fr(r
, "copy key");
765 sshkey_free(authctxt
->auth_method_key
);
766 authctxt
->auth_method_key
= dup
;
771 /* If authenticated, make sure we don't accept this key again */
772 if ((r
= sshkey_from_private(key
, &dup
)) != 0)
773 fatal_fr(r
, "copy key");
774 if (authctxt
->nprev_keys
>= INT_MAX
||
775 (tmp
= recallocarray(authctxt
->prev_keys
, authctxt
->nprev_keys
,
776 authctxt
->nprev_keys
+ 1, sizeof(*authctxt
->prev_keys
))) == NULL
)
777 fatal_f("reallocarray failed");
778 authctxt
->prev_keys
= tmp
;
779 authctxt
->prev_keys
[authctxt
->nprev_keys
] = dup
;
780 authctxt
->nprev_keys
++;
784 /* Checks whether a key has already been previously used for authentication */
786 auth2_key_already_used(Authctxt
*authctxt
, const struct sshkey
*key
)
791 for (i
= 0; i
< authctxt
->nprev_keys
; i
++) {
792 if (sshkey_equal_public(key
, authctxt
->prev_keys
[i
])) {
793 fp
= sshkey_fingerprint(authctxt
->prev_keys
[i
],
794 options
.fingerprint_hash
, SSH_FP_DEFAULT
);
795 debug3_f("key already used: %s %s",
796 sshkey_type(authctxt
->prev_keys
[i
]),
797 fp
== NULL
? "UNKNOWN" : fp
);
806 * Updates authctxt->session_info with details of authentication. Should be
807 * whenever an authentication method succeeds.
810 auth2_update_session_info(Authctxt
*authctxt
, const char *method
,
811 const char *submethod
)
815 if (authctxt
->session_info
== NULL
) {
816 if ((authctxt
->session_info
= sshbuf_new()) == NULL
)
817 fatal_f("sshbuf_new");
820 /* Append method[/submethod] */
821 if ((r
= sshbuf_putf(authctxt
->session_info
, "%s%s%s",
822 method
, submethod
== NULL
? "" : "/",
823 submethod
== NULL
? "" : submethod
)) != 0)
824 fatal_fr(r
, "append method");
826 /* Append key if present */
827 if (authctxt
->auth_method_key
!= NULL
) {
828 if ((r
= sshbuf_put_u8(authctxt
->session_info
, ' ')) != 0 ||
829 (r
= sshkey_format_text(authctxt
->auth_method_key
,
830 authctxt
->session_info
)) != 0)
831 fatal_fr(r
, "append key");
834 if (authctxt
->auth_method_info
!= NULL
) {
835 /* Ensure no ambiguity here */
836 if (strchr(authctxt
->auth_method_info
, '\n') != NULL
)
837 fatal_f("auth_method_info contains \\n");
838 if ((r
= sshbuf_put_u8(authctxt
->session_info
, ' ')) != 0 ||
839 (r
= sshbuf_putf(authctxt
->session_info
, "%s",
840 authctxt
->auth_method_info
)) != 0) {
841 fatal_fr(r
, "append method info");
844 if ((r
= sshbuf_put_u8(authctxt
->session_info
, '\n')) != 0)
845 fatal_fr(r
, "append");