]> andersk Git - openssh.git/blame - sshconnect2.c
- stevesk@cvs.openbsd.org 2001/04/03 13:56:11
[openssh.git] / sshconnect2.c
CommitLineData
a306f2dd 1/*
2 * Copyright (c) 2000 Markus Friedl. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
a306f2dd 12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 */
24
25#include "includes.h"
1ad64a93 26RCSID("$OpenBSD: sshconnect2.c,v 1.60 2001/03/29 21:06:21 stevesk Exp $");
a306f2dd 27
28#include <openssl/bn.h>
a306f2dd 29#include <openssl/md5.h>
30#include <openssl/dh.h>
31#include <openssl/hmac.h>
32
33#include "ssh.h"
42f11eb2 34#include "ssh2.h"
a306f2dd 35#include "xmalloc.h"
36#include "rsa.h"
37#include "buffer.h"
38#include "packet.h"
a306f2dd 39#include "uidswap.h"
40#include "compat.h"
a306f2dd 41#include "bufaux.h"
42f11eb2 42#include "cipher.h"
a306f2dd 43#include "kex.h"
44#include "myproposal.h"
45#include "key.h"
a306f2dd 46#include "sshconnect.h"
47#include "authfile.h"
94ec8c6b 48#include "cli.h"
db1cd2f3 49#include "dh.h"
188adeb2 50#include "dispatch.h"
2e73a022 51#include "authfd.h"
42f11eb2 52#include "log.h"
53#include "readconf.h"
54#include "readpass.h"
cab80f75 55#include "match.h"
a306f2dd 56
94ec8c6b 57void ssh_dh1_client(Kex *, char *, struct sockaddr *, Buffer *, Buffer *);
58void ssh_dhgex_client(Kex *, char *, struct sockaddr *, Buffer *, Buffer *);
59
a306f2dd 60/* import */
61extern char *client_version_string;
62extern char *server_version_string;
63extern Options options;
64
65/*
66 * SSH2 key exchange
67 */
68
1e3b8b07 69u_char *session_id2 = NULL;
a306f2dd 70int session_id2_len = 0;
71
72void
94ec8c6b 73ssh_kex2(char *host, struct sockaddr *hostaddr)
74{
75 int i, plen;
76 Kex *kex;
77 Buffer *client_kexinit, *server_kexinit;
78 char *sprop[PROPOSAL_MAX];
79
c523303b 80 if (options.ciphers == (char *)-1) {
81 log("No valid ciphers for protocol version 2 given, using defaults.");
82 options.ciphers = NULL;
94ec8c6b 83 }
84 if (options.ciphers != NULL) {
85 myproposal[PROPOSAL_ENC_ALGS_CTOS] =
86 myproposal[PROPOSAL_ENC_ALGS_STOC] = options.ciphers;
87 }
1ad64a93 88 myproposal[PROPOSAL_ENC_ALGS_CTOS] =
89 compat_cipher_proposal(myproposal[PROPOSAL_ENC_ALGS_CTOS]);
90 myproposal[PROPOSAL_ENC_ALGS_STOC] =
91 compat_cipher_proposal(myproposal[PROPOSAL_ENC_ALGS_STOC]);
94ec8c6b 92 if (options.compression) {
b2552997 93 myproposal[PROPOSAL_COMP_ALGS_CTOS] =
94ec8c6b 94 myproposal[PROPOSAL_COMP_ALGS_STOC] = "zlib";
95 } else {
b2552997 96 myproposal[PROPOSAL_COMP_ALGS_CTOS] =
94ec8c6b 97 myproposal[PROPOSAL_COMP_ALGS_STOC] = "none";
98 }
b2552997 99 if (options.macs != NULL) {
100 myproposal[PROPOSAL_MAC_ALGS_CTOS] =
101 myproposal[PROPOSAL_MAC_ALGS_STOC] = options.macs;
102 }
94ec8c6b 103
104 /* buffers with raw kexinit messages */
105 server_kexinit = xmalloc(sizeof(*server_kexinit));
106 buffer_init(server_kexinit);
107 client_kexinit = kex_init(myproposal);
108
109 /* algorithm negotiation */
110 kex_exchange_kexinit(client_kexinit, server_kexinit, sprop);
111 kex = kex_choose_conf(myproposal, sprop, 0);
112 for (i = 0; i < PROPOSAL_MAX; i++)
113 xfree(sprop[i]);
114
115 /* server authentication and session key agreement */
116 switch(kex->kex_type) {
117 case DH_GRP1_SHA1:
118 ssh_dh1_client(kex, host, hostaddr,
119 client_kexinit, server_kexinit);
120 break;
121 case DH_GEX_SHA1:
122 ssh_dhgex_client(kex, host, hostaddr, client_kexinit,
123 server_kexinit);
124 break;
125 default:
126 fatal("Unsupported key exchange %d", kex->kex_type);
127 }
128
129 buffer_free(client_kexinit);
130 buffer_free(server_kexinit);
131 xfree(client_kexinit);
132 xfree(server_kexinit);
133
134 debug("Wait SSH2_MSG_NEWKEYS.");
135 packet_read_expect(&plen, SSH2_MSG_NEWKEYS);
136 packet_done();
137 debug("GOT SSH2_MSG_NEWKEYS.");
138
139 debug("send SSH2_MSG_NEWKEYS.");
140 packet_start(SSH2_MSG_NEWKEYS);
141 packet_send();
142 packet_write_wait();
143 debug("done: send SSH2_MSG_NEWKEYS.");
144
145#ifdef DEBUG_KEXDH
146 /* send 1st encrypted/maced/compressed message */
147 packet_start(SSH2_MSG_IGNORE);
148 packet_put_cstring("markus");
149 packet_send();
150 packet_write_wait();
151#endif
152 debug("done: KEX2.");
153}
154
155/* diffie-hellman-group1-sha1 */
156
157void
2b87da3b 158ssh_dh1_client(Kex *kex, char *host, struct sockaddr *hostaddr,
94ec8c6b 159 Buffer *client_kexinit, Buffer *server_kexinit)
a306f2dd 160{
188adeb2 161#ifdef DEBUG_KEXDH
162 int i;
163#endif
71276795 164 int plen, dlen;
1e3b8b07 165 u_int klen, kout;
a306f2dd 166 char *signature = NULL;
1e3b8b07 167 u_int slen;
a306f2dd 168 char *server_host_key_blob = NULL;
169 Key *server_host_key;
1e3b8b07 170 u_int sbloblen;
a306f2dd 171 DH *dh;
172 BIGNUM *dh_server_pub = 0;
173 BIGNUM *shared_secret = 0;
1e3b8b07 174 u_char *kbuf;
175 u_char *hash;
a306f2dd 176
a306f2dd 177 debug("Sending SSH2_MSG_KEXDH_INIT.");
a306f2dd 178 /* generate and send 'e', client DH public key */
179 dh = dh_new_group1();
7de5b06b 180 dh_gen_key(dh, kex->we_need * 8);
a306f2dd 181 packet_start(SSH2_MSG_KEXDH_INIT);
182 packet_put_bignum2(dh->pub_key);
183 packet_send();
184 packet_write_wait();
185
186#ifdef DEBUG_KEXDH
187 fprintf(stderr, "\np= ");
188adeb2 188 BN_print_fp(stderr, dh->p);
a306f2dd 189 fprintf(stderr, "\ng= ");
188adeb2 190 BN_print_fp(stderr, dh->g);
a306f2dd 191 fprintf(stderr, "\npub= ");
188adeb2 192 BN_print_fp(stderr, dh->pub_key);
a306f2dd 193 fprintf(stderr, "\n");
194 DHparams_print_fp(stderr, dh);
195#endif
196
197 debug("Wait SSH2_MSG_KEXDH_REPLY.");
198
71276795 199 packet_read_expect(&plen, SSH2_MSG_KEXDH_REPLY);
a306f2dd 200
201 debug("Got SSH2_MSG_KEXDH_REPLY.");
202
203 /* key, cert */
204 server_host_key_blob = packet_get_string(&sbloblen);
fa08c86b 205 server_host_key = key_from_blob(server_host_key_blob, sbloblen);
a306f2dd 206 if (server_host_key == NULL)
207 fatal("cannot decode server_host_key_blob");
208
209 check_host_key(host, hostaddr, server_host_key,
94ec8c6b 210 options.user_hostfile2, options.system_hostfile2);
a306f2dd 211
212 /* DH paramter f, server public DH key */
213 dh_server_pub = BN_new();
214 if (dh_server_pub == NULL)
215 fatal("dh_server_pub == NULL");
216 packet_get_bignum2(dh_server_pub, &dlen);
217
218#ifdef DEBUG_KEXDH
219 fprintf(stderr, "\ndh_server_pub= ");
188adeb2 220 BN_print_fp(stderr, dh_server_pub);
a306f2dd 221 fprintf(stderr, "\n");
222 debug("bits %d", BN_num_bits(dh_server_pub));
223#endif
224
225 /* signed H */
226 signature = packet_get_string(&slen);
227 packet_done();
228
229 if (!dh_pub_is_valid(dh, dh_server_pub))
230 packet_disconnect("bad server public DH value");
231
232 klen = DH_size(dh);
233 kbuf = xmalloc(klen);
234 kout = DH_compute_key(kbuf, dh_server_pub, dh);
235#ifdef DEBUG_KEXDH
236 debug("shared secret: len %d/%d", klen, kout);
237 fprintf(stderr, "shared secret == ");
238 for (i = 0; i< kout; i++)
239 fprintf(stderr, "%02x", (kbuf[i])&0xff);
240 fprintf(stderr, "\n");
241#endif
242 shared_secret = BN_new();
243
244 BN_bin2bn(kbuf, kout, shared_secret);
245 memset(kbuf, 0, klen);
246 xfree(kbuf);
247
248 /* calc and verify H */
249 hash = kex_hash(
250 client_version_string,
251 server_version_string,
252 buffer_ptr(client_kexinit), buffer_len(client_kexinit),
253 buffer_ptr(server_kexinit), buffer_len(server_kexinit),
254 server_host_key_blob, sbloblen,
255 dh->pub_key,
256 dh_server_pub,
257 shared_secret
258 );
259 xfree(server_host_key_blob);
71276795 260 DH_free(dh);
53a24016 261 BN_free(dh_server_pub);
a306f2dd 262#ifdef DEBUG_KEXDH
263 fprintf(stderr, "hash == ");
264 for (i = 0; i< 20; i++)
265 fprintf(stderr, "%02x", (hash[i])&0xff);
266 fprintf(stderr, "\n");
267#endif
1e3b8b07 268 if (key_verify(server_host_key, (u_char *)signature, slen, hash, 20) != 1)
fa08c86b 269 fatal("key_verify failed for server_host_key");
a306f2dd 270 key_free(server_host_key);
53a24016 271 xfree(signature);
a306f2dd 272
273 kex_derive_keys(kex, hash, shared_secret);
53a24016 274 BN_clear_free(shared_secret);
a306f2dd 275 packet_set_kex(kex);
276
a306f2dd 277 /* save session id */
278 session_id2_len = 20;
279 session_id2 = xmalloc(session_id2_len);
280 memcpy(session_id2, hash, session_id2_len);
71276795 281}
282
94ec8c6b 283/* diffie-hellman-group-exchange-sha1 */
284
285/*
286 * Estimates the group order for a Diffie-Hellman group that has an
287 * attack complexity approximately the same as O(2**bits). Estimate
288 * with: O(exp(1.9223 * (ln q)^(1/3) (ln ln q)^(2/3)))
289 */
290
291int
292dh_estimate(int bits)
293{
2b87da3b 294
94ec8c6b 295 if (bits < 64)
296 return (512); /* O(2**63) */
297 if (bits < 128)
298 return (1024); /* O(2**86) */
299 if (bits < 192)
300 return (2048); /* O(2**116) */
301 return (4096); /* O(2**156) */
302}
303
71276795 304void
94ec8c6b 305ssh_dhgex_client(Kex *kex, char *host, struct sockaddr *hostaddr,
306 Buffer *client_kexinit, Buffer *server_kexinit)
71276795 307{
94ec8c6b 308#ifdef DEBUG_KEXDH
309 int i;
310#endif
311 int plen, dlen;
1e3b8b07 312 u_int klen, kout;
94ec8c6b 313 char *signature = NULL;
db1cd2f3 314 u_int slen, nbits, min, max;
94ec8c6b 315 char *server_host_key_blob = NULL;
316 Key *server_host_key;
1e3b8b07 317 u_int sbloblen;
94ec8c6b 318 DH *dh;
319 BIGNUM *dh_server_pub = 0;
320 BIGNUM *shared_secret = 0;
321 BIGNUM *p = 0, *g = 0;
1e3b8b07 322 u_char *kbuf;
323 u_char *hash;
71276795 324
7de5b06b 325 nbits = dh_estimate(kex->we_need * 8);
71276795 326
db1cd2f3 327 if (datafellows & SSH_OLD_DHGEX) {
328 debug("Sending SSH2_MSG_KEX_DH_GEX_REQUEST_OLD.");
329
330 /* Old GEX request */
331 packet_start(SSH2_MSG_KEX_DH_GEX_REQUEST_OLD);
332 packet_put_int(nbits);
333 min = DH_GRP_MIN;
334 max = DH_GRP_MAX;
335 } else {
336 debug("Sending SSH2_MSG_KEX_DH_GEX_REQUEST.");
337
338 /* New GEX request */
339 min = DH_GRP_MIN;
86b878d5 340 max = DH_GRP_MAX;
db1cd2f3 341
342 packet_start(SSH2_MSG_KEX_DH_GEX_REQUEST);
343 packet_put_int(min);
344 packet_put_int(nbits);
345 packet_put_int(max);
346 }
94ec8c6b 347 packet_send();
348 packet_write_wait();
71276795 349
94ec8c6b 350#ifdef DEBUG_KEXDH
db1cd2f3 351 fprintf(stderr, "\nmin = %d, nbits = %d, max = %d", min, nbits, max);
94ec8c6b 352#endif
71276795 353
94ec8c6b 354 debug("Wait SSH2_MSG_KEX_DH_GEX_GROUP.");
71276795 355
94ec8c6b 356 packet_read_expect(&plen, SSH2_MSG_KEX_DH_GEX_GROUP);
a306f2dd 357
94ec8c6b 358 debug("Got SSH2_MSG_KEX_DH_GEX_GROUP.");
a306f2dd 359
94ec8c6b 360 if ((p = BN_new()) == NULL)
361 fatal("BN_new");
362 packet_get_bignum2(p, &dlen);
363 if ((g = BN_new()) == NULL)
364 fatal("BN_new");
365 packet_get_bignum2(g, &dlen);
db1cd2f3 366
367 if (BN_num_bits(p) < min || BN_num_bits(p) > max)
368 fatal("DH_GEX group out of range: %d !< %d !< %d",
369 min, BN_num_bits(p), max);
370
42f11eb2 371 dh = dh_new_group(g, p);
a306f2dd 372
7de5b06b 373 dh_gen_key(dh, kex->we_need * 8);
3e1caa83 374
a306f2dd 375#ifdef DEBUG_KEXDH
94ec8c6b 376 fprintf(stderr, "\np= ");
377 BN_print_fp(stderr, dh->p);
378 fprintf(stderr, "\ng= ");
379 BN_print_fp(stderr, dh->g);
380 fprintf(stderr, "\npub= ");
381 BN_print_fp(stderr, dh->pub_key);
382 fprintf(stderr, "\n");
383 DHparams_print_fp(stderr, dh);
384#endif
385
386 debug("Sending SSH2_MSG_KEX_DH_GEX_INIT.");
387 /* generate and send 'e', client DH public key */
388 packet_start(SSH2_MSG_KEX_DH_GEX_INIT);
389 packet_put_bignum2(dh->pub_key);
a306f2dd 390 packet_send();
391 packet_write_wait();
94ec8c6b 392
393 debug("Wait SSH2_MSG_KEX_DH_GEX_REPLY.");
394
395 packet_read_expect(&plen, SSH2_MSG_KEX_DH_GEX_REPLY);
396
397 debug("Got SSH2_MSG_KEXDH_REPLY.");
398
399 /* key, cert */
400 server_host_key_blob = packet_get_string(&sbloblen);
fa08c86b 401 server_host_key = key_from_blob(server_host_key_blob, sbloblen);
94ec8c6b 402 if (server_host_key == NULL)
403 fatal("cannot decode server_host_key_blob");
404
405 check_host_key(host, hostaddr, server_host_key,
406 options.user_hostfile2, options.system_hostfile2);
407
408 /* DH paramter f, server public DH key */
409 dh_server_pub = BN_new();
410 if (dh_server_pub == NULL)
411 fatal("dh_server_pub == NULL");
412 packet_get_bignum2(dh_server_pub, &dlen);
413
414#ifdef DEBUG_KEXDH
415 fprintf(stderr, "\ndh_server_pub= ");
416 BN_print_fp(stderr, dh_server_pub);
417 fprintf(stderr, "\n");
418 debug("bits %d", BN_num_bits(dh_server_pub));
a306f2dd 419#endif
94ec8c6b 420
421 /* signed H */
422 signature = packet_get_string(&slen);
423 packet_done();
424
425 if (!dh_pub_is_valid(dh, dh_server_pub))
426 packet_disconnect("bad server public DH value");
427
428 klen = DH_size(dh);
429 kbuf = xmalloc(klen);
430 kout = DH_compute_key(kbuf, dh_server_pub, dh);
431#ifdef DEBUG_KEXDH
432 debug("shared secret: len %d/%d", klen, kout);
433 fprintf(stderr, "shared secret == ");
434 for (i = 0; i< kout; i++)
435 fprintf(stderr, "%02x", (kbuf[i])&0xff);
436 fprintf(stderr, "\n");
437#endif
438 shared_secret = BN_new();
439
440 BN_bin2bn(kbuf, kout, shared_secret);
441 memset(kbuf, 0, klen);
442 xfree(kbuf);
443
4047d868 444 if (datafellows & SSH_OLD_DHGEX) {
445 /* These values are not included in the hash */
446 min = -1;
447 max = -1;
448 }
449
94ec8c6b 450 /* calc and verify H */
451 hash = kex_hash_gex(
452 client_version_string,
453 server_version_string,
454 buffer_ptr(client_kexinit), buffer_len(client_kexinit),
455 buffer_ptr(server_kexinit), buffer_len(server_kexinit),
456 server_host_key_blob, sbloblen,
4047d868 457 min, nbits, max,
458 dh->p, dh->g,
94ec8c6b 459 dh->pub_key,
460 dh_server_pub,
461 shared_secret
462 );
463 xfree(server_host_key_blob);
464 DH_free(dh);
53a24016 465 BN_free(dh_server_pub);
94ec8c6b 466#ifdef DEBUG_KEXDH
467 fprintf(stderr, "hash == ");
468 for (i = 0; i< 20; i++)
469 fprintf(stderr, "%02x", (hash[i])&0xff);
470 fprintf(stderr, "\n");
471#endif
1e3b8b07 472 if (key_verify(server_host_key, (u_char *)signature, slen, hash, 20) != 1)
fa08c86b 473 fatal("key_verify failed for server_host_key");
94ec8c6b 474 key_free(server_host_key);
53a24016 475 xfree(signature);
94ec8c6b 476
477 kex_derive_keys(kex, hash, shared_secret);
53a24016 478 BN_clear_free(shared_secret);
94ec8c6b 479 packet_set_kex(kex);
480
481 /* save session id */
482 session_id2_len = 20;
483 session_id2 = xmalloc(session_id2_len);
484 memcpy(session_id2, hash, session_id2_len);
a306f2dd 485}
71276795 486
a306f2dd 487/*
488 * Authenticate user
489 */
188adeb2 490
491typedef struct Authctxt Authctxt;
492typedef struct Authmethod Authmethod;
493
494typedef int sign_cb_fn(
495 Authctxt *authctxt, Key *key,
1e3b8b07 496 u_char **sigp, int *lenp, u_char *data, int datalen);
188adeb2 497
498struct Authctxt {
499 const char *server_user;
500 const char *host;
501 const char *service;
502 AuthenticationConnection *agent;
188adeb2 503 Authmethod *method;
94ec8c6b 504 int success;
fee56204 505 char *authlist;
506 Key *last_key;
507 sign_cb_fn *last_key_sign;
508 int last_key_hint;
188adeb2 509};
510struct Authmethod {
511 char *name; /* string to compare against server's list */
512 int (*userauth)(Authctxt *authctxt);
513 int *enabled; /* flag in option struct that enables method */
514 int *batch_flag; /* flag in option struct that disables method */
515};
516
517void input_userauth_success(int type, int plen, void *ctxt);
518void input_userauth_failure(int type, int plen, void *ctxt);
9616313f 519void input_userauth_banner(int type, int plen, void *ctxt);
188adeb2 520void input_userauth_error(int type, int plen, void *ctxt);
94ec8c6b 521void input_userauth_info_req(int type, int plen, void *ctxt);
fee56204 522void input_userauth_pk_ok(int type, int plen, void *ctxt);
94ec8c6b 523
524int userauth_none(Authctxt *authctxt);
188adeb2 525int userauth_pubkey(Authctxt *authctxt);
526int userauth_passwd(Authctxt *authctxt);
94ec8c6b 527int userauth_kbdint(Authctxt *authctxt);
188adeb2 528
fee56204 529void userauth(Authctxt *authctxt, char *authlist);
530
531int
532sign_and_send_pubkey(Authctxt *authctxt, Key *k,
533 sign_cb_fn *sign_callback);
534void clear_auth_state(Authctxt *authctxt);
535
94ec8c6b 536Authmethod *authmethod_get(char *authlist);
537Authmethod *authmethod_lookup(const char *name);
cab80f75 538char *authmethods_get(void);
188adeb2 539
540Authmethod authmethods[] = {
541 {"publickey",
542 userauth_pubkey,
fa08c86b 543 &options.pubkey_authentication,
188adeb2 544 NULL},
545 {"password",
546 userauth_passwd,
547 &options.password_authentication,
548 &options.batch_mode},
94ec8c6b 549 {"keyboard-interactive",
550 userauth_kbdint,
551 &options.kbd_interactive_authentication,
552 &options.batch_mode},
553 {"none",
554 userauth_none,
555 NULL,
556 NULL},
188adeb2 557 {NULL, NULL, NULL, NULL}
558};
559
560void
561ssh_userauth2(const char *server_user, char *host)
562{
563 Authctxt authctxt;
564 int type;
565 int plen;
566
d464095c 567 if (options.challenge_reponse_authentication)
568 options.kbd_interactive_authentication = 1;
569
188adeb2 570 debug("send SSH2_MSG_SERVICE_REQUEST");
571 packet_start(SSH2_MSG_SERVICE_REQUEST);
572 packet_put_cstring("ssh-userauth");
573 packet_send();
574 packet_write_wait();
575 type = packet_read(&plen);
576 if (type != SSH2_MSG_SERVICE_ACCEPT) {
577 fatal("denied SSH2_MSG_SERVICE_ACCEPT: %d", type);
578 }
579 if (packet_remaining() > 0) {
580 char *reply = packet_get_string(&plen);
581 debug("service_accept: %s", reply);
582 xfree(reply);
188adeb2 583 } else {
584 debug("buggy server: service_accept w/o service");
585 }
586 packet_done();
587 debug("got SSH2_MSG_SERVICE_ACCEPT");
588
cab80f75 589 if (options.preferred_authentications == NULL)
590 options.preferred_authentications = authmethods_get();
591
188adeb2 592 /* setup authentication context */
593 authctxt.agent = ssh_get_authentication_connection();
594 authctxt.server_user = server_user;
595 authctxt.host = host;
596 authctxt.service = "ssh-connection"; /* service name */
597 authctxt.success = 0;
94ec8c6b 598 authctxt.method = authmethod_lookup("none");
fee56204 599 authctxt.authlist = NULL;
94ec8c6b 600 if (authctxt.method == NULL)
601 fatal("ssh_userauth2: internal error: cannot send userauth none request");
188adeb2 602
603 /* initial userauth request */
94ec8c6b 604 userauth_none(&authctxt);
188adeb2 605
606 dispatch_init(&input_userauth_error);
607 dispatch_set(SSH2_MSG_USERAUTH_SUCCESS, &input_userauth_success);
608 dispatch_set(SSH2_MSG_USERAUTH_FAILURE, &input_userauth_failure);
9616313f 609 dispatch_set(SSH2_MSG_USERAUTH_BANNER, &input_userauth_banner);
188adeb2 610 dispatch_run(DISPATCH_BLOCK, &authctxt.success, &authctxt); /* loop until success */
611
612 if (authctxt.agent != NULL)
613 ssh_close_authentication_connection(authctxt.agent);
614
8abcdba4 615 debug("ssh-userauth2 successful: method %s", authctxt.method->name);
188adeb2 616}
617void
fee56204 618userauth(Authctxt *authctxt, char *authlist)
619{
620 if (authlist == NULL) {
621 authlist = authctxt->authlist;
622 } else {
623 if (authctxt->authlist)
624 xfree(authctxt->authlist);
625 authctxt->authlist = authlist;
626 }
627 for (;;) {
628 Authmethod *method = authmethod_get(authlist);
629 if (method == NULL)
630 fatal("Permission denied (%s).", authlist);
631 authctxt->method = method;
632 if (method->userauth(authctxt) != 0) {
633 debug2("we sent a %s packet, wait for reply", method->name);
634 break;
635 } else {
636 debug2("we did not send a packet, disable method");
637 method->enabled = NULL;
638 }
639 }
640}
641void
188adeb2 642input_userauth_error(int type, int plen, void *ctxt)
643{
9616313f 644 fatal("input_userauth_error: bad message during authentication: "
645 "type %d", type);
646}
647void
648input_userauth_banner(int type, int plen, void *ctxt)
649{
650 char *msg, *lang;
651 debug3("input_userauth_banner");
652 msg = packet_get_string(NULL);
653 lang = packet_get_string(NULL);
654 fprintf(stderr, "%s", msg);
655 xfree(msg);
656 xfree(lang);
188adeb2 657}
658void
659input_userauth_success(int type, int plen, void *ctxt)
660{
661 Authctxt *authctxt = ctxt;
662 if (authctxt == NULL)
663 fatal("input_userauth_success: no authentication context");
fee56204 664 if (authctxt->authlist)
665 xfree(authctxt->authlist);
666 clear_auth_state(authctxt);
188adeb2 667 authctxt->success = 1; /* break out */
668}
669void
670input_userauth_failure(int type, int plen, void *ctxt)
671{
188adeb2 672 Authctxt *authctxt = ctxt;
673 char *authlist = NULL;
674 int partial;
188adeb2 675
676 if (authctxt == NULL)
677 fatal("input_userauth_failure: no authentication context");
678
94ec8c6b 679 authlist = packet_get_string(NULL);
188adeb2 680 partial = packet_get_char();
681 packet_done();
682
683 if (partial != 0)
f72e01a5 684 log("Authenticated with partial success.");
188adeb2 685 debug("authentications that can continue: %s", authlist);
686
fee56204 687 clear_auth_state(authctxt);
688 userauth(authctxt, authlist);
689}
690void
691input_userauth_pk_ok(int type, int plen, void *ctxt)
692{
693 Authctxt *authctxt = ctxt;
694 Key *key = NULL;
695 Buffer b;
696 int alen, blen, pktype, sent = 0;
22138a36 697 char *pkalg, *pkblob, *fp;
fee56204 698
699 if (authctxt == NULL)
700 fatal("input_userauth_pk_ok: no authentication context");
701 if (datafellows & SSH_BUG_PKOK) {
702 /* this is similar to SSH_BUG_PKAUTH */
703 debug2("input_userauth_pk_ok: SSH_BUG_PKOK");
704 pkblob = packet_get_string(&blen);
705 buffer_init(&b);
706 buffer_append(&b, pkblob, blen);
707 pkalg = buffer_get_string(&b, &alen);
708 buffer_free(&b);
709 } else {
710 pkalg = packet_get_string(&alen);
711 pkblob = packet_get_string(&blen);
712 }
713 packet_done();
714
715 debug("input_userauth_pk_ok: pkalg %s blen %d lastkey %p hint %d",
716 pkalg, blen, authctxt->last_key, authctxt->last_key_hint);
717
718 do {
719 if (authctxt->last_key == NULL ||
720 authctxt->last_key_sign == NULL) {
721 debug("no last key or no sign cb");
188adeb2 722 break;
188adeb2 723 }
fee56204 724 if ((pktype = key_type_from_name(pkalg)) == KEY_UNSPEC) {
725 debug("unknown pkalg %s", pkalg);
726 break;
727 }
728 if ((key = key_from_blob(pkblob, blen)) == NULL) {
729 debug("no key from blob. pkalg %s", pkalg);
730 break;
731 }
22138a36 732 fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX);
733 debug2("input_userauth_pk_ok: fp %s", fp);
734 xfree(fp);
fee56204 735 if (!key_equal(key, authctxt->last_key)) {
736 debug("key != last_key");
737 break;
738 }
739 sent = sign_and_send_pubkey(authctxt, key,
740 authctxt->last_key_sign);
741 } while(0);
742
743 if (key != NULL)
744 key_free(key);
745 xfree(pkalg);
746 xfree(pkblob);
747
748 /* unregister */
749 clear_auth_state(authctxt);
750 dispatch_set(SSH2_MSG_USERAUTH_PK_OK, NULL);
751
752 /* try another method if we did not send a packet*/
753 if (sent == 0)
754 userauth(authctxt, NULL);
755
188adeb2 756}
757
94ec8c6b 758int
759userauth_none(Authctxt *authctxt)
760{
761 /* initial userauth request */
762 packet_start(SSH2_MSG_USERAUTH_REQUEST);
763 packet_put_cstring(authctxt->server_user);
764 packet_put_cstring(authctxt->service);
765 packet_put_cstring(authctxt->method->name);
766 packet_send();
94ec8c6b 767 return 1;
768}
769
a306f2dd 770int
188adeb2 771userauth_passwd(Authctxt *authctxt)
a306f2dd 772{
1d1ffb87 773 static int attempt = 0;
a306f2dd 774 char prompt[80];
775 char *password;
776
fa649821 777 if (attempt++ >= options.number_of_password_prompts)
1d1ffb87 778 return 0;
779
fa649821 780 if(attempt != 1)
781 error("Permission denied, please try again.");
782
f72e01a5 783 snprintf(prompt, sizeof(prompt), "%.30s@%.128s's password: ",
188adeb2 784 authctxt->server_user, authctxt->host);
a306f2dd 785 password = read_passphrase(prompt, 0);
786 packet_start(SSH2_MSG_USERAUTH_REQUEST);
188adeb2 787 packet_put_cstring(authctxt->server_user);
788 packet_put_cstring(authctxt->service);
94ec8c6b 789 packet_put_cstring(authctxt->method->name);
a306f2dd 790 packet_put_char(0);
a6215e53 791 packet_put_cstring(password);
a306f2dd 792 memset(password, 0, strlen(password));
793 xfree(password);
a6215e53 794 packet_inject_ignore(64);
a306f2dd 795 packet_send();
a306f2dd 796 return 1;
797}
798
fee56204 799void
800clear_auth_state(Authctxt *authctxt)
801{
802 /* XXX clear authentication state */
803 if (authctxt->last_key != NULL && authctxt->last_key_hint == -1) {
804 debug3("clear_auth_state: key_free %p", authctxt->last_key);
805 key_free(authctxt->last_key);
806 }
807 authctxt->last_key = NULL;
808 authctxt->last_key_hint = -2;
809 authctxt->last_key_sign = NULL;
810}
811
2e73a022 812int
188adeb2 813sign_and_send_pubkey(Authctxt *authctxt, Key *k, sign_cb_fn *sign_callback)
a306f2dd 814{
815 Buffer b;
1e3b8b07 816 u_char *blob, *signature;
a306f2dd 817 int bloblen, slen;
74fc9186 818 int skip = 0;
2e73a022 819 int ret = -1;
94ec8c6b 820 int have_sig = 1;
a306f2dd 821
cbc5abf9 822 debug3("sign_and_send_pubkey");
fee56204 823
fa08c86b 824 if (key_to_blob(k, &blob, &bloblen) == 0) {
825 /* we cannot handle this key */
cbc5abf9 826 debug3("sign_and_send_pubkey: cannot handle key");
fa08c86b 827 return 0;
828 }
a306f2dd 829 /* data to be signed */
830 buffer_init(&b);
33de75a3 831 if (datafellows & SSH_OLD_SESSIONID) {
74fc9186 832 buffer_append(&b, session_id2, session_id2_len);
2b87da3b 833 skip = session_id2_len;
33de75a3 834 } else {
835 buffer_put_string(&b, session_id2, session_id2_len);
836 skip = buffer_len(&b);
74fc9186 837 }
a306f2dd 838 buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST);
188adeb2 839 buffer_put_cstring(&b, authctxt->server_user);
d0c832f3 840 buffer_put_cstring(&b,
cbc5abf9 841 datafellows & SSH_BUG_PKSERVICE ?
d0c832f3 842 "ssh-userauth" :
188adeb2 843 authctxt->service);
cbc5abf9 844 if (datafellows & SSH_BUG_PKAUTH) {
845 buffer_put_char(&b, have_sig);
846 } else {
847 buffer_put_cstring(&b, authctxt->method->name);
848 buffer_put_char(&b, have_sig);
2b87da3b 849 buffer_put_cstring(&b, key_ssh_name(k));
cbc5abf9 850 }
a306f2dd 851 buffer_put_string(&b, blob, bloblen);
a306f2dd 852
853 /* generate signature */
fee56204 854 ret = (*sign_callback)(authctxt, k, &signature, &slen,
855 buffer_ptr(&b), buffer_len(&b));
2e73a022 856 if (ret == -1) {
857 xfree(blob);
858 buffer_free(&b);
859 return 0;
860 }
fa08c86b 861#ifdef DEBUG_PK
a306f2dd 862 buffer_dump(&b);
863#endif
cbc5abf9 864 if (datafellows & SSH_BUG_PKSERVICE) {
d0c832f3 865 buffer_clear(&b);
866 buffer_append(&b, session_id2, session_id2_len);
fee56204 867 skip = session_id2_len;
d0c832f3 868 buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST);
188adeb2 869 buffer_put_cstring(&b, authctxt->server_user);
870 buffer_put_cstring(&b, authctxt->service);
94ec8c6b 871 buffer_put_cstring(&b, authctxt->method->name);
872 buffer_put_char(&b, have_sig);
cbc5abf9 873 if (!(datafellows & SSH_BUG_PKAUTH))
2b87da3b 874 buffer_put_cstring(&b, key_ssh_name(k));
d0c832f3 875 buffer_put_string(&b, blob, bloblen);
876 }
877 xfree(blob);
fee56204 878
a306f2dd 879 /* append signature */
880 buffer_put_string(&b, signature, slen);
881 xfree(signature);
882
883 /* skip session id and packet type */
74fc9186 884 if (buffer_len(&b) < skip + 1)
188adeb2 885 fatal("userauth_pubkey: internal error");
74fc9186 886 buffer_consume(&b, skip + 1);
a306f2dd 887
888 /* put remaining data from buffer into packet */
889 packet_start(SSH2_MSG_USERAUTH_REQUEST);
890 packet_put_raw(buffer_ptr(&b), buffer_len(&b));
891 buffer_free(&b);
a306f2dd 892 packet_send();
2e73a022 893
894 return 1;
4c8722d9 895}
896
897int
fee56204 898send_pubkey_test(Authctxt *authctxt, Key *k, sign_cb_fn *sign_callback,
899 int hint)
4c8722d9 900{
fee56204 901 u_char *blob;
902 int bloblen, have_sig = 0;
4c8722d9 903
fee56204 904 debug3("send_pubkey_test");
905
906 if (key_to_blob(k, &blob, &bloblen) == 0) {
907 /* we cannot handle this key */
908 debug3("send_pubkey_test: cannot handle key");
4c8722d9 909 return 0;
910 }
fee56204 911 /* register callback for USERAUTH_PK_OK message */
912 authctxt->last_key_sign = sign_callback;
913 authctxt->last_key_hint = hint;
914 authctxt->last_key = k;
915 dispatch_set(SSH2_MSG_USERAUTH_PK_OK, &input_userauth_pk_ok);
4c8722d9 916
fee56204 917 packet_start(SSH2_MSG_USERAUTH_REQUEST);
918 packet_put_cstring(authctxt->server_user);
919 packet_put_cstring(authctxt->service);
920 packet_put_cstring(authctxt->method->name);
921 packet_put_char(have_sig);
922 if (!(datafellows & SSH_BUG_PKAUTH))
923 packet_put_cstring(key_ssh_name(k));
924 packet_put_string(blob, bloblen);
925 xfree(blob);
926 packet_send();
927 return 1;
928}
929
930Key *
931load_identity_file(char *filename)
932{
933 Key *private;
934 char prompt[300], *passphrase;
aeaa3d9e 935 int quit, i;
d156519a 936 struct stat st;
fee56204 937
d156519a 938 if (stat(filename, &st) < 0) {
939 debug3("no such identity: %s", filename);
940 return NULL;
941 }
aeaa3d9e 942 private = key_load_private_type(KEY_UNSPEC, filename, "", NULL);
943 if (private == NULL) {
944 if (options.batch_mode)
fee56204 945 return NULL;
4c8722d9 946 snprintf(prompt, sizeof prompt,
fa08c86b 947 "Enter passphrase for key '%.100s': ", filename);
188adeb2 948 for (i = 0; i < options.number_of_password_prompts; i++) {
949 passphrase = read_passphrase(prompt, 0);
950 if (strcmp(passphrase, "") != 0) {
aeaa3d9e 951 private = key_load_private_type(KEY_UNSPEC, filename,
952 passphrase, NULL);
fee56204 953 quit = 0;
188adeb2 954 } else {
955 debug2("no passphrase given, try next key");
fee56204 956 quit = 1;
188adeb2 957 }
958 memset(passphrase, 0, strlen(passphrase));
959 xfree(passphrase);
aeaa3d9e 960 if (private != NULL || quit)
188adeb2 961 break;
962 debug2("bad passphrase given, try again...");
963 }
4c8722d9 964 }
fee56204 965 return private;
966}
967
968int
969identity_sign_cb(Authctxt *authctxt, Key *key, u_char **sigp, int *lenp,
970 u_char *data, int datalen)
971{
972 Key *private;
973 int idx, ret;
974
975 idx = authctxt->last_key_hint;
976 if (idx < 0)
977 return -1;
978 private = load_identity_file(options.identity_files[idx]);
979 if (private == NULL)
980 return -1;
981 ret = key_sign(private, sigp, lenp, data, datalen);
982 key_free(private);
2e73a022 983 return ret;
984}
985
1e3b8b07 986int agent_sign_cb(Authctxt *authctxt, Key *key, u_char **sigp, int *lenp,
987 u_char *data, int datalen)
2e73a022 988{
188adeb2 989 return ssh_agent_sign(authctxt->agent, key, sigp, lenp, data, datalen);
2e73a022 990}
991
fee56204 992int key_sign_cb(Authctxt *authctxt, Key *key, u_char **sigp, int *lenp,
993 u_char *data, int datalen)
994{
995 return key_sign(key, sigp, lenp, data, datalen);
996}
997
2e73a022 998int
188adeb2 999userauth_pubkey_agent(Authctxt *authctxt)
2e73a022 1000{
1001 static int called = 0;
fa08c86b 1002 int ret = 0;
2e73a022 1003 char *comment;
1004 Key *k;
2e73a022 1005
1006 if (called == 0) {
fa08c86b 1007 if (ssh_get_num_identities(authctxt->agent, 2) == 0)
1008 debug2("userauth_pubkey_agent: no keys at all");
188adeb2 1009 called = 1;
2e73a022 1010 }
fa08c86b 1011 k = ssh_get_next_identity(authctxt->agent, &comment, 2);
188adeb2 1012 if (k == NULL) {
fa08c86b 1013 debug2("userauth_pubkey_agent: no more keys");
1014 } else {
fee56204 1015 debug("userauth_pubkey_agent: testing agent key %s", comment);
fa08c86b 1016 xfree(comment);
fee56204 1017 ret = send_pubkey_test(authctxt, k, agent_sign_cb, -1);
1018 if (ret == 0)
1019 key_free(k);
188adeb2 1020 }
fa08c86b 1021 if (ret == 0)
1022 debug2("userauth_pubkey_agent: no message sent");
2e73a022 1023 return ret;
a306f2dd 1024}
1025
188adeb2 1026int
1027userauth_pubkey(Authctxt *authctxt)
a306f2dd 1028{
188adeb2 1029 static int idx = 0;
1030 int sent = 0;
fee56204 1031 Key *key;
1032 char *filename;
188adeb2 1033
fa08c86b 1034 if (authctxt->agent != NULL) {
1035 do {
1036 sent = userauth_pubkey_agent(authctxt);
1037 } while(!sent && authctxt->agent->howmany > 0);
1038 }
1039 while (!sent && idx < options.num_identity_files) {
fee56204 1040 key = options.identity_keys[idx];
1041 filename = options.identity_files[idx];
1042 if (key == NULL) {
1043 debug("try privkey: %s", filename);
1044 key = load_identity_file(filename);
1045 if (key != NULL) {
1046 sent = sign_and_send_pubkey(authctxt, key,
1047 key_sign_cb);
1048 key_free(key);
1049 }
1050 } else if (key->type != KEY_RSA1) {
1051 debug("try pubkey: %s", filename);
1052 sent = send_pubkey_test(authctxt, key,
1053 identity_sign_cb, idx);
1054 }
fa08c86b 1055 idx++;
1056 }
188adeb2 1057 return sent;
1058}
a306f2dd 1059
94ec8c6b 1060/*
1061 * Send userauth request message specifying keyboard-interactive method.
1062 */
1063int
1064userauth_kbdint(Authctxt *authctxt)
1065{
1066 static int attempt = 0;
1067
1068 if (attempt++ >= options.number_of_password_prompts)
1069 return 0;
1070
1071 debug2("userauth_kbdint");
1072 packet_start(SSH2_MSG_USERAUTH_REQUEST);
1073 packet_put_cstring(authctxt->server_user);
1074 packet_put_cstring(authctxt->service);
1075 packet_put_cstring(authctxt->method->name);
1076 packet_put_cstring(""); /* lang */
1077 packet_put_cstring(options.kbd_interactive_devices ?
1078 options.kbd_interactive_devices : "");
1079 packet_send();
94ec8c6b 1080
1081 dispatch_set(SSH2_MSG_USERAUTH_INFO_REQUEST, &input_userauth_info_req);
1082 return 1;
1083}
1084
1085/*
f72e01a5 1086 * parse INFO_REQUEST, prompt user and send INFO_RESPONSE
94ec8c6b 1087 */
1088void
1089input_userauth_info_req(int type, int plen, void *ctxt)
1090{
1091 Authctxt *authctxt = ctxt;
f72e01a5 1092 char *name, *inst, *lang, *prompt, *response;
1e3b8b07 1093 u_int num_prompts, i;
94ec8c6b 1094 int echo = 0;
1095
1096 debug2("input_userauth_info_req");
1097
1098 if (authctxt == NULL)
1099 fatal("input_userauth_info_req: no authentication context");
1100
1101 name = packet_get_string(NULL);
1102 inst = packet_get_string(NULL);
1103 lang = packet_get_string(NULL);
94ec8c6b 1104 if (strlen(name) > 0)
1105 cli_mesg(name);
94ec8c6b 1106 if (strlen(inst) > 0)
1107 cli_mesg(inst);
f72e01a5 1108 xfree(name);
94ec8c6b 1109 xfree(inst);
f72e01a5 1110 xfree(lang);
94ec8c6b 1111
1112 num_prompts = packet_get_int();
1113 /*
1114 * Begin to build info response packet based on prompts requested.
1115 * We commit to providing the correct number of responses, so if
1116 * further on we run into a problem that prevents this, we have to
1117 * be sure and clean this up and send a correct error response.
1118 */
1119 packet_start(SSH2_MSG_USERAUTH_INFO_RESPONSE);
1120 packet_put_int(num_prompts);
1121
1122 for (i = 0; i < num_prompts; i++) {
1123 prompt = packet_get_string(NULL);
1124 echo = packet_get_char();
1125
1126 response = cli_prompt(prompt, echo);
1127
a6215e53 1128 packet_put_cstring(response);
94ec8c6b 1129 memset(response, 0, strlen(response));
1130 xfree(response);
1131 xfree(prompt);
1132 }
1133 packet_done(); /* done with parsing incoming message. */
1134
a6215e53 1135 packet_inject_ignore(64);
94ec8c6b 1136 packet_send();
94ec8c6b 1137}
a306f2dd 1138
188adeb2 1139/* find auth method */
1140
188adeb2 1141/*
1142 * given auth method name, if configurable options permit this method fill
1143 * in auth_ident field and return true, otherwise return false.
1144 */
1145int
1146authmethod_is_enabled(Authmethod *method)
1147{
1148 if (method == NULL)
1149 return 0;
1150 /* return false if options indicate this method is disabled */
1151 if (method->enabled == NULL || *method->enabled == 0)
1152 return 0;
1153 /* return false if batch mode is enabled but method needs interactive mode */
1154 if (method->batch_flag != NULL && *method->batch_flag != 0)
1155 return 0;
1156 return 1;
1157}
a306f2dd 1158
188adeb2 1159Authmethod *
1160authmethod_lookup(const char *name)
1161{
1162 Authmethod *method = NULL;
1163 if (name != NULL)
1164 for (method = authmethods; method->name != NULL; method++)
1165 if (strcmp(name, method->name) == 0)
1166 return method;
1167 debug2("Unrecognized authentication method name: %s", name ? name : "NULL");
1168 return NULL;
1169}
1170
cab80f75 1171/* XXX internal state */
1172static Authmethod *current = NULL;
1173static char *supported = NULL;
1174static char *preferred = NULL;
188adeb2 1175/*
1176 * Given the authentication method list sent by the server, return the
1177 * next method we should try. If the server initially sends a nil list,
cab80f75 1178 * use a built-in default list.
2b87da3b 1179 */
188adeb2 1180Authmethod *
1181authmethod_get(char *authlist)
1182{
cab80f75 1183
1184 char *name = NULL;
1185 int next;
2b87da3b 1186
188adeb2 1187 /* Use a suitable default if we're passed a nil list. */
1188 if (authlist == NULL || strlen(authlist) == 0)
cab80f75 1189 authlist = options.preferred_authentications;
1190
1191 if (supported == NULL || strcmp(authlist, supported) != 0) {
1192 debug3("start over, passed a different list %s", authlist);
1193 if (supported != NULL)
1194 xfree(supported);
1195 supported = xstrdup(authlist);
1196 preferred = options.preferred_authentications;
1197 debug3("preferred %s", preferred);
1198 current = NULL;
1199 } else if (current != NULL && authmethod_is_enabled(current))
1200 return current;
188adeb2 1201
cab80f75 1202 for (;;) {
1203 if ((name = match_list(preferred, supported, &next)) == NULL) {
1204 debug("no more auth methods to try");
1205 current = NULL;
1206 return NULL;
1207 }
1208 preferred += next;
94ec8c6b 1209 debug3("authmethod_lookup %s", name);
cab80f75 1210 debug3("remaining preferred: %s", preferred);
1211 if ((current = authmethod_lookup(name)) != NULL &&
1212 authmethod_is_enabled(current)) {
94ec8c6b 1213 debug3("authmethod_is_enabled %s", name);
cab80f75 1214 debug("next auth method to try is %s", name);
1215 return current;
94ec8c6b 1216 }
a306f2dd 1217 }
cab80f75 1218}
a22aff1f 1219
a22aff1f 1220
cab80f75 1221#define DELIM ","
1222char *
1223authmethods_get(void)
1224{
1225 Authmethod *method = NULL;
1226 char buf[1024];
1227
1228 buf[0] = '\0';
1229 for (method = authmethods; method->name != NULL; method++) {
1230 if (authmethod_is_enabled(method)) {
1231 if (buf[0] != '\0')
1232 strlcat(buf, DELIM, sizeof buf);
1233 strlcat(buf, method->name, sizeof buf);
1234 }
1235 }
1236 return xstrdup(buf);
a306f2dd 1237}
This page took 2.191757 seconds and 5 git commands to generate.