]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - crypto/openssh/auth2-pubkey.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / crypto / openssh / auth2-pubkey.c
1 /* $OpenBSD: auth2-pubkey.c,v 1.38 2013/06/21 00:34:49 djm Exp $ */
2 /*
3  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "includes.h"
27
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <sys/wait.h>
31
32 #include <errno.h>
33 #include <fcntl.h>
34 #ifdef HAVE_PATHS_H
35 # include <paths.h>
36 #endif
37 #include <pwd.h>
38 #include <signal.h>
39 #include <stdio.h>
40 #include <stdarg.h>
41 #include <string.h>
42 #include <time.h>
43 #include <unistd.h>
44
45 #include "xmalloc.h"
46 #include "ssh.h"
47 #include "ssh2.h"
48 #include "packet.h"
49 #include "buffer.h"
50 #include "log.h"
51 #include "servconf.h"
52 #include "compat.h"
53 #include "key.h"
54 #include "hostfile.h"
55 #include "auth.h"
56 #include "pathnames.h"
57 #include "uidswap.h"
58 #include "auth-options.h"
59 #include "canohost.h"
60 #ifdef GSSAPI
61 #include "ssh-gss.h"
62 #endif
63 #include "monitor_wrap.h"
64 #include "misc.h"
65 #include "authfile.h"
66 #include "match.h"
67
68 /* import */
69 extern ServerOptions options;
70 extern u_char *session_id2;
71 extern u_int session_id2_len;
72
73 static int
74 userauth_pubkey(Authctxt *authctxt)
75 {
76         Buffer b;
77         Key *key = NULL;
78         char *pkalg, *userstyle;
79         u_char *pkblob, *sig;
80         u_int alen, blen, slen;
81         int have_sig, pktype;
82         int authenticated = 0;
83
84         if (!authctxt->valid) {
85                 debug2("userauth_pubkey: disabled because of invalid user");
86                 return 0;
87         }
88         have_sig = packet_get_char();
89         if (datafellows & SSH_BUG_PKAUTH) {
90                 debug2("userauth_pubkey: SSH_BUG_PKAUTH");
91                 /* no explicit pkalg given */
92                 pkblob = packet_get_string(&blen);
93                 buffer_init(&b);
94                 buffer_append(&b, pkblob, blen);
95                 /* so we have to extract the pkalg from the pkblob */
96                 pkalg = buffer_get_string(&b, &alen);
97                 buffer_free(&b);
98         } else {
99                 pkalg = packet_get_string(&alen);
100                 pkblob = packet_get_string(&blen);
101         }
102         pktype = key_type_from_name(pkalg);
103         if (pktype == KEY_UNSPEC) {
104                 /* this is perfectly legal */
105                 logit("userauth_pubkey: unsupported public key algorithm: %s",
106                     pkalg);
107                 goto done;
108         }
109         key = key_from_blob(pkblob, blen);
110         if (key == NULL) {
111                 error("userauth_pubkey: cannot decode key: %s", pkalg);
112                 goto done;
113         }
114         if (key->type != pktype) {
115                 error("userauth_pubkey: type mismatch for decoded key "
116                     "(received %d, expected %d)", key->type, pktype);
117                 goto done;
118         }
119         if (have_sig) {
120                 sig = packet_get_string(&slen);
121                 packet_check_eom();
122                 buffer_init(&b);
123                 if (datafellows & SSH_OLD_SESSIONID) {
124                         buffer_append(&b, session_id2, session_id2_len);
125                 } else {
126                         buffer_put_string(&b, session_id2, session_id2_len);
127                 }
128                 /* reconstruct packet */
129                 buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST);
130                 xasprintf(&userstyle, "%s%s%s", authctxt->user,
131                     authctxt->style ? ":" : "",
132                     authctxt->style ? authctxt->style : "");
133                 buffer_put_cstring(&b, userstyle);
134                 free(userstyle);
135                 buffer_put_cstring(&b,
136                     datafellows & SSH_BUG_PKSERVICE ?
137                     "ssh-userauth" :
138                     authctxt->service);
139                 if (datafellows & SSH_BUG_PKAUTH) {
140                         buffer_put_char(&b, have_sig);
141                 } else {
142                         buffer_put_cstring(&b, "publickey");
143                         buffer_put_char(&b, have_sig);
144                         buffer_put_cstring(&b, pkalg);
145                 }
146                 buffer_put_string(&b, pkblob, blen);
147 #ifdef DEBUG_PK
148                 buffer_dump(&b);
149 #endif
150                 pubkey_auth_info(authctxt, key, NULL);
151
152                 /* test for correct signature */
153                 authenticated = 0;
154                 if (PRIVSEP(user_key_allowed(authctxt->pw, key)) &&
155                     PRIVSEP(key_verify(key, sig, slen, buffer_ptr(&b),
156                     buffer_len(&b))) == 1)
157                         authenticated = 1;
158                 buffer_free(&b);
159                 free(sig);
160         } else {
161                 debug("test whether pkalg/pkblob are acceptable");
162                 packet_check_eom();
163
164                 /* XXX fake reply and always send PK_OK ? */
165                 /*
166                  * XXX this allows testing whether a user is allowed
167                  * to login: if you happen to have a valid pubkey this
168                  * message is sent. the message is NEVER sent at all
169                  * if a user is not allowed to login. is this an
170                  * issue? -markus
171                  */
172                 if (PRIVSEP(user_key_allowed(authctxt->pw, key))) {
173                         packet_start(SSH2_MSG_USERAUTH_PK_OK);
174                         packet_put_string(pkalg, alen);
175                         packet_put_string(pkblob, blen);
176                         packet_send();
177                         packet_write_wait();
178                         authctxt->postponed = 1;
179                 }
180         }
181         if (authenticated != 1)
182                 auth_clear_options();
183 done:
184         debug2("userauth_pubkey: authenticated %d pkalg %s", authenticated, pkalg);
185         if (key != NULL)
186                 key_free(key);
187         free(pkalg);
188         free(pkblob);
189         return authenticated;
190 }
191
192 void
193 pubkey_auth_info(Authctxt *authctxt, const Key *key, const char *fmt, ...)
194 {
195         char *fp, *extra;
196         va_list ap;
197         int i;
198
199         extra = NULL;
200         if (fmt != NULL) {
201                 va_start(ap, fmt);
202                 i = vasprintf(&extra, fmt, ap);
203                 va_end(ap);
204                 if (i < 0 || extra == NULL)
205                         fatal("%s: vasprintf failed", __func__);        
206         }
207
208         if (key_is_cert(key)) {
209                 fp = key_fingerprint(key->cert->signature_key,
210                     SSH_FP_MD5, SSH_FP_HEX);
211                 auth_info(authctxt, "%s ID %s (serial %llu) CA %s %s%s%s", 
212                     key_type(key), key->cert->key_id,
213                     (unsigned long long)key->cert->serial,
214                     key_type(key->cert->signature_key), fp,
215                     extra == NULL ? "" : ", ", extra == NULL ? "" : extra);
216                 free(fp);
217         } else {
218                 fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX);
219                 auth_info(authctxt, "%s %s%s%s", key_type(key), fp,
220                     extra == NULL ? "" : ", ", extra == NULL ? "" : extra);
221                 free(fp);
222         }
223         free(extra);
224 }
225
226 static int
227 match_principals_option(const char *principal_list, struct KeyCert *cert)
228 {
229         char *result;
230         u_int i;
231
232         /* XXX percent_expand() sequences for authorized_principals? */
233
234         for (i = 0; i < cert->nprincipals; i++) {
235                 if ((result = match_list(cert->principals[i],
236                     principal_list, NULL)) != NULL) {
237                         debug3("matched principal from key options \"%.100s\"",
238                             result);
239                         free(result);
240                         return 1;
241                 }
242         }
243         return 0;
244 }
245
246 static int
247 match_principals_file(char *file, struct passwd *pw, struct KeyCert *cert)
248 {
249         FILE *f;
250         char line[SSH_MAX_PUBKEY_BYTES], *cp, *ep, *line_opts;
251         u_long linenum = 0;
252         u_int i;
253
254         temporarily_use_uid(pw);
255         debug("trying authorized principals file %s", file);
256         if ((f = auth_openprincipals(file, pw, options.strict_modes)) == NULL) {
257                 restore_uid();
258                 return 0;
259         }
260         while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) {
261                 /* Skip leading whitespace. */
262                 for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
263                         ;
264                 /* Skip blank and comment lines. */
265                 if ((ep = strchr(cp, '#')) != NULL)
266                         *ep = '\0';
267                 if (!*cp || *cp == '\n')
268                         continue;
269                 /* Trim trailing whitespace. */
270                 ep = cp + strlen(cp) - 1;
271                 while (ep > cp && (*ep == '\n' || *ep == ' ' || *ep == '\t'))
272                         *ep-- = '\0';
273                 /*
274                  * If the line has internal whitespace then assume it has
275                  * key options.
276                  */
277                 line_opts = NULL;
278                 if ((ep = strrchr(cp, ' ')) != NULL ||
279                     (ep = strrchr(cp, '\t')) != NULL) {
280                         for (; *ep == ' ' || *ep == '\t'; ep++)
281                                 ;
282                         line_opts = cp;
283                         cp = ep;
284                 }
285                 for (i = 0; i < cert->nprincipals; i++) {
286                         if (strcmp(cp, cert->principals[i]) == 0) {
287                                 debug3("matched principal \"%.100s\" "
288                                     "from file \"%s\" on line %lu",
289                                     cert->principals[i], file, linenum);
290                                 if (auth_parse_options(pw, line_opts,
291                                     file, linenum) != 1)
292                                         continue;
293                                 fclose(f);
294                                 restore_uid();
295                                 return 1;
296                         }
297                 }
298         }
299         fclose(f);
300         restore_uid();
301         return 0;
302 }
303
304 /*
305  * Checks whether key is allowed in authorized_keys-format file,
306  * returns 1 if the key is allowed or 0 otherwise.
307  */
308 static int
309 check_authkeys_file(FILE *f, char *file, Key* key, struct passwd *pw)
310 {
311         char line[SSH_MAX_PUBKEY_BYTES];
312         const char *reason;
313         int found_key = 0;
314         u_long linenum = 0;
315         Key *found;
316         char *fp;
317
318         found_key = 0;
319
320         found = NULL;
321         while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) {
322                 char *cp, *key_options = NULL;
323                 if (found != NULL)
324                         key_free(found);
325                 found = key_new(key_is_cert(key) ? KEY_UNSPEC : key->type);
326                 auth_clear_options();
327
328                 /* Skip leading whitespace, empty and comment lines. */
329                 for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
330                         ;
331                 if (!*cp || *cp == '\n' || *cp == '#')
332                         continue;
333
334                 if (key_read(found, &cp) != 1) {
335                         /* no key?  check if there are options for this key */
336                         int quoted = 0;
337                         debug2("user_key_allowed: check options: '%s'", cp);
338                         key_options = cp;
339                         for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) {
340                                 if (*cp == '\\' && cp[1] == '"')
341                                         cp++;   /* Skip both */
342                                 else if (*cp == '"')
343                                         quoted = !quoted;
344                         }
345                         /* Skip remaining whitespace. */
346                         for (; *cp == ' ' || *cp == '\t'; cp++)
347                                 ;
348                         if (key_read(found, &cp) != 1) {
349                                 debug2("user_key_allowed: advance: '%s'", cp);
350                                 /* still no key?  advance to next line*/
351                                 continue;
352                         }
353                 }
354                 if (key_is_cert(key)) {
355                         if (!key_equal(found, key->cert->signature_key))
356                                 continue;
357                         if (auth_parse_options(pw, key_options, file,
358                             linenum) != 1)
359                                 continue;
360                         if (!key_is_cert_authority)
361                                 continue;
362                         fp = key_fingerprint(found, SSH_FP_MD5,
363                             SSH_FP_HEX);
364                         debug("matching CA found: file %s, line %lu, %s %s",
365                             file, linenum, key_type(found), fp);
366                         /*
367                          * If the user has specified a list of principals as
368                          * a key option, then prefer that list to matching
369                          * their username in the certificate principals list.
370                          */
371                         if (authorized_principals != NULL &&
372                             !match_principals_option(authorized_principals,
373                             key->cert)) {
374                                 reason = "Certificate does not contain an "
375                                     "authorized principal";
376  fail_reason:
377                                 free(fp);
378                                 error("%s", reason);
379                                 auth_debug_add("%s", reason);
380                                 continue;
381                         }
382                         if (key_cert_check_authority(key, 0, 0,
383                             authorized_principals == NULL ? pw->pw_name : NULL,
384                             &reason) != 0)
385                                 goto fail_reason;
386                         if (auth_cert_options(key, pw) != 0) {
387                                 free(fp);
388                                 continue;
389                         }
390                         verbose("Accepted certificate ID \"%s\" "
391                             "signed by %s CA %s via %s", key->cert->key_id,
392                             key_type(found), fp, file);
393                         free(fp);
394                         found_key = 1;
395                         break;
396                 } else if (key_equal(found, key)) {
397                         if (auth_parse_options(pw, key_options, file,
398                             linenum) != 1)
399                                 continue;
400                         if (key_is_cert_authority)
401                                 continue;
402                         found_key = 1;
403                         fp = key_fingerprint(found, SSH_FP_MD5, SSH_FP_HEX);
404                         debug("matching key found: file %s, line %lu %s %s",
405                             file, linenum, key_type(found), fp);
406                         free(fp);
407                         break;
408                 }
409         }
410         if (found != NULL)
411                 key_free(found);
412         if (!found_key)
413                 debug2("key not found");
414         return found_key;
415 }
416
417 /* Authenticate a certificate key against TrustedUserCAKeys */
418 static int
419 user_cert_trusted_ca(struct passwd *pw, Key *key)
420 {
421         char *ca_fp, *principals_file = NULL;
422         const char *reason;
423         int ret = 0;
424
425         if (!key_is_cert(key) || options.trusted_user_ca_keys == NULL)
426                 return 0;
427
428         ca_fp = key_fingerprint(key->cert->signature_key,
429             SSH_FP_MD5, SSH_FP_HEX);
430
431         if (key_in_file(key->cert->signature_key,
432             options.trusted_user_ca_keys, 1) != 1) {
433                 debug2("%s: CA %s %s is not listed in %s", __func__,
434                     key_type(key->cert->signature_key), ca_fp,
435                     options.trusted_user_ca_keys);
436                 goto out;
437         }
438         /*
439          * If AuthorizedPrincipals is in use, then compare the certificate
440          * principals against the names in that file rather than matching
441          * against the username.
442          */
443         if ((principals_file = authorized_principals_file(pw)) != NULL) {
444                 if (!match_principals_file(principals_file, pw, key->cert)) {
445                         reason = "Certificate does not contain an "
446                             "authorized principal";
447  fail_reason:
448                         error("%s", reason);
449                         auth_debug_add("%s", reason);
450                         goto out;
451                 }
452         }
453         if (key_cert_check_authority(key, 0, 1,
454             principals_file == NULL ? pw->pw_name : NULL, &reason) != 0)
455                 goto fail_reason;
456         if (auth_cert_options(key, pw) != 0)
457                 goto out;
458
459         verbose("Accepted certificate ID \"%s\" signed by %s CA %s via %s",
460             key->cert->key_id, key_type(key->cert->signature_key), ca_fp,
461             options.trusted_user_ca_keys);
462         ret = 1;
463
464  out:
465         free(principals_file);
466         free(ca_fp);
467         return ret;
468 }
469
470 /*
471  * Checks whether key is allowed in file.
472  * returns 1 if the key is allowed or 0 otherwise.
473  */
474 static int
475 user_key_allowed2(struct passwd *pw, Key *key, char *file)
476 {
477         FILE *f;
478         int found_key = 0;
479
480         /* Temporarily use the user's uid. */
481         temporarily_use_uid(pw);
482
483         debug("trying public key file %s", file);
484         if ((f = auth_openkeyfile(file, pw, options.strict_modes)) != NULL) {
485                 found_key = check_authkeys_file(f, file, key, pw);
486                 fclose(f);
487         }
488
489         restore_uid();
490         return found_key;
491 }
492
493 /*
494  * Checks whether key is allowed in output of command.
495  * returns 1 if the key is allowed or 0 otherwise.
496  */
497 static int
498 user_key_command_allowed2(struct passwd *user_pw, Key *key)
499 {
500         FILE *f;
501         int ok, found_key = 0;
502         struct passwd *pw;
503         struct stat st;
504         int status, devnull, p[2], i;
505         pid_t pid;
506         char *username, errmsg[512];
507
508         if (options.authorized_keys_command == NULL ||
509             options.authorized_keys_command[0] != '/')
510                 return 0;
511
512         if (options.authorized_keys_command_user == NULL) {
513                 error("No user for AuthorizedKeysCommand specified, skipping");
514                 return 0;
515         }
516
517         username = percent_expand(options.authorized_keys_command_user,
518             "u", user_pw->pw_name, (char *)NULL);
519         pw = getpwnam(username);
520         if (pw == NULL) {
521                 error("AuthorizedKeysCommandUser \"%s\" not found: %s",
522                     username, strerror(errno));
523                 free(username);
524                 return 0;
525         }
526         free(username);
527
528         temporarily_use_uid(pw);
529
530         if (stat(options.authorized_keys_command, &st) < 0) {
531                 error("Could not stat AuthorizedKeysCommand \"%s\": %s",
532                     options.authorized_keys_command, strerror(errno));
533                 goto out;
534         }
535         if (auth_secure_path(options.authorized_keys_command, &st, NULL, 0,
536             errmsg, sizeof(errmsg)) != 0) {
537                 error("Unsafe AuthorizedKeysCommand: %s", errmsg);
538                 goto out;
539         }
540
541         if (pipe(p) != 0) {
542                 error("%s: pipe: %s", __func__, strerror(errno));
543                 goto out;
544         }
545
546         debug3("Running AuthorizedKeysCommand: \"%s %s\" as \"%s\"",
547             options.authorized_keys_command, user_pw->pw_name, pw->pw_name);
548
549         /*
550          * Don't want to call this in the child, where it can fatal() and
551          * run cleanup_exit() code.
552          */
553         restore_uid();
554
555         switch ((pid = fork())) {
556         case -1: /* error */
557                 error("%s: fork: %s", __func__, strerror(errno));
558                 close(p[0]);
559                 close(p[1]);
560                 return 0;
561         case 0: /* child */
562                 for (i = 0; i < NSIG; i++)
563                         signal(i, SIG_DFL);
564
565                 if ((devnull = open(_PATH_DEVNULL, O_RDWR)) == -1) {
566                         error("%s: open %s: %s", __func__, _PATH_DEVNULL,
567                             strerror(errno));
568                         _exit(1);
569                 }
570                 /* Keep stderr around a while longer to catch errors */
571                 if (dup2(devnull, STDIN_FILENO) == -1 ||
572                     dup2(p[1], STDOUT_FILENO) == -1) {
573                         error("%s: dup2: %s", __func__, strerror(errno));
574                         _exit(1);
575                 }
576                 closefrom(STDERR_FILENO + 1);
577
578                 /* Don't use permanently_set_uid() here to avoid fatal() */
579                 if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0) {
580                         error("setresgid %u: %s", (u_int)pw->pw_gid,
581                             strerror(errno));
582                         _exit(1);
583                 }
584                 if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) != 0) {
585                         error("setresuid %u: %s", (u_int)pw->pw_uid,
586                             strerror(errno));
587                         _exit(1);
588                 }
589                 /* stdin is pointed to /dev/null at this point */
590                 if (dup2(STDIN_FILENO, STDERR_FILENO) == -1) {
591                         error("%s: dup2: %s", __func__, strerror(errno));
592                         _exit(1);
593                 }
594
595                 execl(options.authorized_keys_command,
596                     options.authorized_keys_command, user_pw->pw_name, NULL);
597
598                 error("AuthorizedKeysCommand %s exec failed: %s",
599                     options.authorized_keys_command, strerror(errno));
600                 _exit(127);
601         default: /* parent */
602                 break;
603         }
604
605         temporarily_use_uid(pw);
606
607         close(p[1]);
608         if ((f = fdopen(p[0], "r")) == NULL) {
609                 error("%s: fdopen: %s", __func__, strerror(errno));
610                 close(p[0]);
611                 /* Don't leave zombie child */
612                 kill(pid, SIGTERM);
613                 while (waitpid(pid, NULL, 0) == -1 && errno == EINTR)
614                         ;
615                 goto out;
616         }
617         ok = check_authkeys_file(f, options.authorized_keys_command, key, pw);
618         fclose(f);
619
620         while (waitpid(pid, &status, 0) == -1) {
621                 if (errno != EINTR) {
622                         error("%s: waitpid: %s", __func__, strerror(errno));
623                         goto out;
624                 }
625         }
626         if (WIFSIGNALED(status)) {
627                 error("AuthorizedKeysCommand %s exited on signal %d",
628                     options.authorized_keys_command, WTERMSIG(status));
629                 goto out;
630         } else if (WEXITSTATUS(status) != 0) {
631                 error("AuthorizedKeysCommand %s returned status %d",
632                     options.authorized_keys_command, WEXITSTATUS(status));
633                 goto out;
634         }
635         found_key = ok;
636  out:
637         restore_uid();
638         return found_key;
639 }
640
641 /*
642  * Check whether key authenticates and authorises the user.
643  */
644 int
645 user_key_allowed(struct passwd *pw, Key *key)
646 {
647         u_int success, i;
648         char *file;
649
650         if (auth_key_is_revoked(key))
651                 return 0;
652         if (key_is_cert(key) && auth_key_is_revoked(key->cert->signature_key))
653                 return 0;
654
655         success = user_cert_trusted_ca(pw, key);
656         if (success)
657                 return success;
658
659         success = user_key_command_allowed2(pw, key);
660         if (success > 0)
661                 return success;
662
663         for (i = 0; !success && i < options.num_authkeys_files; i++) {
664
665                 if (strcasecmp(options.authorized_keys_files[i], "none") == 0)
666                         continue;
667                 file = expand_authorized_keys(
668                     options.authorized_keys_files[i], pw);
669
670                 success = user_key_allowed2(pw, key, file);
671                 free(file);
672         }
673
674         return success;
675 }
676
677 Authmethod method_pubkey = {
678         "publickey",
679         userauth_pubkey,
680         &options.pubkey_authentication
681 };