diff options
| author | SkyperTHC | 2026-03-03 06:28:55 +0000 |
|---|---|---|
| committer | SkyperTHC | 2026-03-03 06:28:55 +0000 |
| commit | 5d3573ef7a109ee70416fe94db098fe6a769a798 (patch) | |
| tree | dc2d5b294c9db8ab2db7433511f94e1c4bb8b698 /other/ssharp/sshconnect2.c | |
| parent | c6c59dc73cc4586357f93ab38ecf459e98675cc5 (diff) | |
packetstorm sync
Diffstat (limited to 'other/ssharp/sshconnect2.c')
| -rw-r--r-- | other/ssharp/sshconnect2.c | 991 |
1 files changed, 991 insertions, 0 deletions
diff --git a/other/ssharp/sshconnect2.c b/other/ssharp/sshconnect2.c new file mode 100644 index 0000000..b46a064 --- /dev/null +++ b/other/ssharp/sshconnect2.c | |||
| @@ -0,0 +1,991 @@ | |||
| 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. | ||
| 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" | ||
| 26 | RCSID("$OpenBSD: sshconnect2.c,v 1.72 2001/04/18 23:43:26 markus Exp $"); | ||
| 27 | |||
| 28 | #include <openssl/bn.h> | ||
| 29 | #include <openssl/md5.h> | ||
| 30 | #include <openssl/dh.h> | ||
| 31 | #include <openssl/hmac.h> | ||
| 32 | |||
| 33 | #include "ssh.h" | ||
| 34 | #include "ssh2.h" | ||
| 35 | #include "xmalloc.h" | ||
| 36 | #include "rsa.h" | ||
| 37 | #include "buffer.h" | ||
| 38 | #include "packet.h" | ||
| 39 | #include "uidswap.h" | ||
| 40 | #include "compat.h" | ||
| 41 | #include "bufaux.h" | ||
| 42 | #include "cipher.h" | ||
| 43 | #include "kex.h" | ||
| 44 | #include "myproposal.h" | ||
| 45 | #include "key.h" | ||
| 46 | #include "sshconnect.h" | ||
| 47 | #include "authfile.h" | ||
| 48 | #include "cli.h" | ||
| 49 | #include "dh.h" | ||
| 50 | #include "authfd.h" | ||
| 51 | #include "log.h" | ||
| 52 | #include "readconf.h" | ||
| 53 | #include "readpass.h" | ||
| 54 | #include "match.h" | ||
| 55 | #include "dispatch.h" | ||
| 56 | #include "canohost.h" | ||
| 57 | |||
| 58 | /* import */ | ||
| 59 | extern char *client_version_string; | ||
| 60 | extern char *server_version_string; | ||
| 61 | extern Options options; | ||
| 62 | |||
| 63 | /* | ||
| 64 | * SSH2 key exchange | ||
| 65 | */ | ||
| 66 | |||
| 67 | u_char *session_id2 = NULL; | ||
| 68 | int session_id2_len = 0; | ||
| 69 | |||
| 70 | char *xxx_host; | ||
| 71 | struct sockaddr *xxx_hostaddr; | ||
| 72 | |||
| 73 | Kex *xxx_kex = NULL; | ||
| 74 | |||
| 75 | int | ||
| 76 | check_host_key_callback(Key *hostkey) | ||
| 77 | { | ||
| 78 | /* SSHARP: dummy */ | ||
| 79 | return 0; | ||
| 80 | } | ||
| 81 | |||
| 82 | void | ||
| 83 | ssh_kex2(char *host, struct sockaddr *hostaddr) | ||
| 84 | { | ||
| 85 | Kex *kex; | ||
| 86 | |||
| 87 | xxx_host = host; | ||
| 88 | xxx_hostaddr = hostaddr; | ||
| 89 | |||
| 90 | if (options.ciphers == (char *)-1) { | ||
| 91 | log("No valid ciphers for protocol version 2 given, using defaults."); | ||
| 92 | options.ciphers = NULL; | ||
| 93 | } | ||
| 94 | if (options.ciphers != NULL) { | ||
| 95 | myproposal[PROPOSAL_ENC_ALGS_CTOS] = | ||
| 96 | myproposal[PROPOSAL_ENC_ALGS_STOC] = options.ciphers; | ||
| 97 | } | ||
| 98 | myproposal[PROPOSAL_ENC_ALGS_CTOS] = | ||
| 99 | compat_cipher_proposal(myproposal[PROPOSAL_ENC_ALGS_CTOS]); | ||
| 100 | myproposal[PROPOSAL_ENC_ALGS_STOC] = | ||
| 101 | compat_cipher_proposal(myproposal[PROPOSAL_ENC_ALGS_STOC]); | ||
| 102 | if (options.compression) { | ||
| 103 | myproposal[PROPOSAL_COMP_ALGS_CTOS] = | ||
| 104 | myproposal[PROPOSAL_COMP_ALGS_STOC] = "zlib"; | ||
| 105 | } else { | ||
| 106 | myproposal[PROPOSAL_COMP_ALGS_CTOS] = | ||
| 107 | myproposal[PROPOSAL_COMP_ALGS_STOC] = "none"; | ||
| 108 | } | ||
| 109 | if (options.macs != NULL) { | ||
| 110 | myproposal[PROPOSAL_MAC_ALGS_CTOS] = | ||
| 111 | myproposal[PROPOSAL_MAC_ALGS_STOC] = options.macs; | ||
| 112 | } | ||
| 113 | if (options.hostkeyalgorithms != NULL) | ||
| 114 | myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = | ||
| 115 | options.hostkeyalgorithms; | ||
| 116 | |||
| 117 | /* start key exchange */ | ||
| 118 | kex = kex_setup(myproposal); | ||
| 119 | kex->client_version_string=client_version_string; | ||
| 120 | kex->server_version_string=server_version_string; | ||
| 121 | kex->check_host_key=&check_host_key_callback; | ||
| 122 | |||
| 123 | xxx_kex = kex; | ||
| 124 | |||
| 125 | dispatch_run(DISPATCH_BLOCK, &kex->done, kex); | ||
| 126 | |||
| 127 | session_id2 = kex->session_id; | ||
| 128 | session_id2_len = kex->session_id_len; | ||
| 129 | |||
| 130 | #ifdef DEBUG_KEXDH | ||
| 131 | /* send 1st encrypted/maced/compressed message */ | ||
| 132 | packet_start(SSH2_MSG_IGNORE); | ||
| 133 | packet_put_cstring("markus"); | ||
| 134 | packet_send(); | ||
| 135 | packet_write_wait(); | ||
| 136 | #endif | ||
| 137 | debug("done: ssh_kex2."); | ||
| 138 | } | ||
| 139 | |||
| 140 | /* | ||
| 141 | * Authenticate user | ||
| 142 | */ | ||
| 143 | |||
| 144 | typedef struct Authctxt Authctxt; | ||
| 145 | typedef struct Authmethod Authmethod; | ||
| 146 | |||
| 147 | typedef int sign_cb_fn( | ||
| 148 | Authctxt *authctxt, Key *key, | ||
| 149 | u_char **sigp, int *lenp, u_char *data, int datalen); | ||
| 150 | |||
| 151 | struct Authctxt { | ||
| 152 | const char *server_user; | ||
| 153 | const char *local_user; | ||
| 154 | const char *pass; | ||
| 155 | const char *host; | ||
| 156 | const char *service; | ||
| 157 | Authmethod *method; | ||
| 158 | int success; | ||
| 159 | char *authlist; | ||
| 160 | /* pubkey */ | ||
| 161 | Key *last_key; | ||
| 162 | sign_cb_fn *last_key_sign; | ||
| 163 | int last_key_hint; | ||
| 164 | AuthenticationConnection *agent; | ||
| 165 | /* hostbased */ | ||
| 166 | Key **keys; | ||
| 167 | int nkeys; | ||
| 168 | }; | ||
| 169 | struct Authmethod { | ||
| 170 | char *name; /* string to compare against server's list */ | ||
| 171 | int (*userauth)(Authctxt *authctxt); | ||
| 172 | int *enabled; /* flag in option struct that enables method */ | ||
| 173 | int *batch_flag; /* flag in option struct that disables method */ | ||
| 174 | }; | ||
| 175 | |||
| 176 | void input_userauth_success(int type, int plen, void *ctxt); | ||
| 177 | void input_userauth_failure(int type, int plen, void *ctxt); | ||
| 178 | void input_userauth_banner(int type, int plen, void *ctxt); | ||
| 179 | void input_userauth_error(int type, int plen, void *ctxt); | ||
| 180 | void input_userauth_info_req(int type, int plen, void *ctxt); | ||
| 181 | void input_userauth_pk_ok(int type, int plen, void *ctxt); | ||
| 182 | |||
| 183 | int userauth_none(Authctxt *authctxt); | ||
| 184 | int userauth_pubkey(Authctxt *authctxt); | ||
| 185 | int userauth_passwd(Authctxt *authctxt); | ||
| 186 | int userauth_kbdint(Authctxt *authctxt); | ||
| 187 | int userauth_hostbased(Authctxt *authctxt); | ||
| 188 | |||
| 189 | void userauth(Authctxt *authctxt, char *authlist); | ||
| 190 | |||
| 191 | int | ||
| 192 | sign_and_send_pubkey(Authctxt *authctxt, Key *k, | ||
| 193 | sign_cb_fn *sign_callback); | ||
| 194 | void clear_auth_state(Authctxt *authctxt); | ||
| 195 | |||
| 196 | Authmethod *authmethod_get(char *authlist); | ||
| 197 | Authmethod *authmethod_lookup(const char *name); | ||
| 198 | char *authmethods_get(void); | ||
| 199 | |||
| 200 | Authmethod authmethods[] = { | ||
| 201 | {"publickey", | ||
| 202 | userauth_pubkey, | ||
| 203 | &options.pubkey_authentication, | ||
| 204 | NULL}, | ||
| 205 | {"password", | ||
| 206 | userauth_passwd, | ||
| 207 | &options.password_authentication, | ||
| 208 | &options.batch_mode}, | ||
| 209 | {"keyboard-interactive", | ||
| 210 | userauth_kbdint, | ||
| 211 | &options.kbd_interactive_authentication, | ||
| 212 | &options.batch_mode}, | ||
| 213 | {"hostbased", | ||
| 214 | userauth_hostbased, | ||
| 215 | &options.hostbased_authentication, | ||
| 216 | NULL}, | ||
| 217 | {"none", | ||
| 218 | userauth_none, | ||
| 219 | NULL, | ||
| 220 | NULL}, | ||
| 221 | {NULL, NULL, NULL, NULL} | ||
| 222 | }; | ||
| 223 | |||
| 224 | void | ||
| 225 | ssh_userauth2(const char *login, const char *pass, char *host, | ||
| 226 | Key **keys, int nkeys) | ||
| 227 | { | ||
| 228 | Authctxt authctxt; | ||
| 229 | int type; | ||
| 230 | int plen; | ||
| 231 | |||
| 232 | if (options.challenge_reponse_authentication) | ||
| 233 | options.kbd_interactive_authentication = 1; | ||
| 234 | |||
| 235 | debug("send SSH2_MSG_SERVICE_REQUEST"); | ||
| 236 | packet_start(SSH2_MSG_SERVICE_REQUEST); | ||
| 237 | packet_put_cstring("ssh-userauth"); | ||
| 238 | packet_send(); | ||
| 239 | packet_write_wait(); | ||
| 240 | type = packet_read(&plen); | ||
| 241 | if (type != SSH2_MSG_SERVICE_ACCEPT) { | ||
| 242 | fatal("denied SSH2_MSG_SERVICE_ACCEPT: %d", type); | ||
| 243 | } | ||
| 244 | if (packet_remaining() > 0) { | ||
| 245 | char *reply = packet_get_string(&plen); | ||
| 246 | debug("service_accept: %s", reply); | ||
| 247 | xfree(reply); | ||
| 248 | } else { | ||
| 249 | debug("buggy server: service_accept w/o service"); | ||
| 250 | } | ||
| 251 | packet_done(); | ||
| 252 | debug("got SSH2_MSG_SERVICE_ACCEPT"); | ||
| 253 | |||
| 254 | if (options.preferred_authentications == NULL) | ||
| 255 | options.preferred_authentications = authmethods_get(); | ||
| 256 | |||
| 257 | /* setup authentication context */ | ||
| 258 | authctxt.agent = ssh_get_authentication_connection(); | ||
| 259 | authctxt.server_user = login; | ||
| 260 | authctxt.local_user = login; | ||
| 261 | authctxt.pass = strdup(pass); | ||
| 262 | authctxt.host = host; | ||
| 263 | |||
| 264 | authctxt.service = "ssh-connection"; /* service name */ | ||
| 265 | authctxt.success = 0; | ||
| 266 | authctxt.method = authmethod_lookup("none"); | ||
| 267 | authctxt.authlist = NULL; | ||
| 268 | authctxt.keys = keys; | ||
| 269 | authctxt.nkeys = nkeys; | ||
| 270 | if (authctxt.method == NULL) | ||
| 271 | fatal("ssh_userauth2: internal error: cannot send userauth none request"); | ||
| 272 | |||
| 273 | /* initial userauth request */ | ||
| 274 | userauth_none(&authctxt); | ||
| 275 | |||
| 276 | dispatch_init(&input_userauth_error); | ||
| 277 | dispatch_set(SSH2_MSG_USERAUTH_SUCCESS, &input_userauth_success); | ||
| 278 | dispatch_set(SSH2_MSG_USERAUTH_FAILURE, &input_userauth_failure); | ||
| 279 | dispatch_set(SSH2_MSG_USERAUTH_BANNER, &input_userauth_banner); | ||
| 280 | dispatch_run(DISPATCH_BLOCK, &authctxt.success, &authctxt); /* loop until success */ | ||
| 281 | |||
| 282 | if (authctxt.agent != NULL) | ||
| 283 | ssh_close_authentication_connection(authctxt.agent); | ||
| 284 | |||
| 285 | debug("ssh-userauth2 successful: method %s", authctxt.method->name); | ||
| 286 | } | ||
| 287 | void | ||
| 288 | userauth(Authctxt *authctxt, char *authlist) | ||
| 289 | { | ||
| 290 | if (authlist == NULL) { | ||
| 291 | authlist = authctxt->authlist; | ||
| 292 | } else { | ||
| 293 | if (authctxt->authlist) | ||
| 294 | xfree(authctxt->authlist); | ||
| 295 | authctxt->authlist = authlist; | ||
| 296 | } | ||
| 297 | for (;;) { | ||
| 298 | Authmethod *method = authmethod_get(authlist); | ||
| 299 | if (method == NULL) | ||
| 300 | fatal("Permission denied (%s).", authlist); | ||
| 301 | authctxt->method = method; | ||
| 302 | if (method->userauth(authctxt) != 0) { | ||
| 303 | debug2("we sent a %s packet, wait for reply", method->name); | ||
| 304 | break; | ||
| 305 | } else { | ||
| 306 | debug2("we did not send a packet, disable method"); | ||
| 307 | method->enabled = NULL; | ||
| 308 | } | ||
| 309 | } | ||
| 310 | } | ||
| 311 | void | ||
| 312 | input_userauth_error(int type, int plen, void *ctxt) | ||
| 313 | { | ||
| 314 | fatal("input_userauth_error: bad message during authentication: " | ||
| 315 | "type %d", type); | ||
| 316 | } | ||
| 317 | void | ||
| 318 | input_userauth_banner(int type, int plen, void *ctxt) | ||
| 319 | { | ||
| 320 | char *msg, *lang; | ||
| 321 | debug3("input_userauth_banner"); | ||
| 322 | msg = packet_get_string(NULL); | ||
| 323 | lang = packet_get_string(NULL); | ||
| 324 | fprintf(stderr, "%s", msg); | ||
| 325 | xfree(msg); | ||
| 326 | xfree(lang); | ||
| 327 | } | ||
| 328 | void | ||
| 329 | input_userauth_success(int type, int plen, void *ctxt) | ||
| 330 | { | ||
| 331 | Authctxt *authctxt = ctxt; | ||
| 332 | if (authctxt == NULL) | ||
| 333 | fatal("input_userauth_success: no authentication context"); | ||
| 334 | if (authctxt->authlist) | ||
| 335 | xfree(authctxt->authlist); | ||
| 336 | clear_auth_state(authctxt); | ||
| 337 | authctxt->success = 1; /* break out */ | ||
| 338 | } | ||
| 339 | void | ||
| 340 | input_userauth_failure(int type, int plen, void *ctxt) | ||
| 341 | { | ||
| 342 | Authctxt *authctxt = ctxt; | ||
| 343 | char *authlist = NULL; | ||
| 344 | int partial; | ||
| 345 | |||
| 346 | if (authctxt == NULL) | ||
| 347 | fatal("input_userauth_failure: no authentication context"); | ||
| 348 | |||
| 349 | authlist = packet_get_string(NULL); | ||
| 350 | partial = packet_get_char(); | ||
| 351 | packet_done(); | ||
| 352 | |||
| 353 | if (partial != 0) | ||
| 354 | log("Authenticated with partial success."); | ||
| 355 | debug("authentications that can continue: %s", authlist); | ||
| 356 | |||
| 357 | clear_auth_state(authctxt); | ||
| 358 | userauth(authctxt, authlist); | ||
| 359 | } | ||
| 360 | void | ||
| 361 | input_userauth_pk_ok(int type, int plen, void *ctxt) | ||
| 362 | { | ||
| 363 | Authctxt *authctxt = ctxt; | ||
| 364 | Key *key = NULL; | ||
| 365 | Buffer b; | ||
| 366 | int alen, blen, sent = 0; | ||
| 367 | char *pkalg, *pkblob, *fp; | ||
| 368 | |||
| 369 | if (authctxt == NULL) | ||
| 370 | fatal("input_userauth_pk_ok: no authentication context"); | ||
| 371 | if (datafellows & SSH_BUG_PKOK) { | ||
| 372 | /* this is similar to SSH_BUG_PKAUTH */ | ||
| 373 | debug2("input_userauth_pk_ok: SSH_BUG_PKOK"); | ||
| 374 | pkblob = packet_get_string(&blen); | ||
| 375 | buffer_init(&b); | ||
| 376 | buffer_append(&b, pkblob, blen); | ||
| 377 | pkalg = buffer_get_string(&b, &alen); | ||
| 378 | buffer_free(&b); | ||
| 379 | } else { | ||
| 380 | pkalg = packet_get_string(&alen); | ||
| 381 | pkblob = packet_get_string(&blen); | ||
| 382 | } | ||
| 383 | packet_done(); | ||
| 384 | |||
| 385 | debug("input_userauth_pk_ok: pkalg %s blen %d lastkey %p hint %d", | ||
| 386 | pkalg, blen, authctxt->last_key, authctxt->last_key_hint); | ||
| 387 | |||
| 388 | do { | ||
| 389 | if (authctxt->last_key == NULL || | ||
| 390 | authctxt->last_key_sign == NULL) { | ||
| 391 | debug("no last key or no sign cb"); | ||
| 392 | break; | ||
| 393 | } | ||
| 394 | if (key_type_from_name(pkalg) == KEY_UNSPEC) { | ||
| 395 | debug("unknown pkalg %s", pkalg); | ||
| 396 | break; | ||
| 397 | } | ||
| 398 | if ((key = key_from_blob(pkblob, blen)) == NULL) { | ||
| 399 | debug("no key from blob. pkalg %s", pkalg); | ||
| 400 | break; | ||
| 401 | } | ||
| 402 | fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); | ||
| 403 | debug2("input_userauth_pk_ok: fp %s", fp); | ||
| 404 | xfree(fp); | ||
| 405 | if (!key_equal(key, authctxt->last_key)) { | ||
| 406 | debug("key != last_key"); | ||
| 407 | break; | ||
| 408 | } | ||
| 409 | sent = sign_and_send_pubkey(authctxt, key, | ||
| 410 | authctxt->last_key_sign); | ||
| 411 | } while(0); | ||
| 412 | |||
| 413 | if (key != NULL) | ||
| 414 | key_free(key); | ||
| 415 | xfree(pkalg); | ||
| 416 | xfree(pkblob); | ||
| 417 | |||
| 418 | /* unregister */ | ||
| 419 | clear_auth_state(authctxt); | ||
| 420 | dispatch_set(SSH2_MSG_USERAUTH_PK_OK, NULL); | ||
| 421 | |||
| 422 | /* try another method if we did not send a packet*/ | ||
| 423 | if (sent == 0) | ||
| 424 | userauth(authctxt, NULL); | ||
| 425 | |||
| 426 | } | ||
| 427 | |||
| 428 | int | ||
| 429 | userauth_none(Authctxt *authctxt) | ||
| 430 | { | ||
| 431 | /* initial userauth request */ | ||
| 432 | packet_start(SSH2_MSG_USERAUTH_REQUEST); | ||
| 433 | packet_put_cstring(authctxt->server_user); | ||
| 434 | packet_put_cstring(authctxt->service); | ||
| 435 | packet_put_cstring(authctxt->method->name); | ||
| 436 | packet_send(); | ||
| 437 | return 1; | ||
| 438 | } | ||
| 439 | |||
| 440 | int | ||
| 441 | userauth_passwd(Authctxt *authctxt) | ||
| 442 | { | ||
| 443 | static int attempt = 0; | ||
| 444 | const char *password; | ||
| 445 | |||
| 446 | if (attempt++ >= options.number_of_password_prompts) | ||
| 447 | return 0; | ||
| 448 | |||
| 449 | if(attempt != 1) | ||
| 450 | error("Permission denied, please try again."); | ||
| 451 | |||
| 452 | password = authctxt->pass; | ||
| 453 | packet_start(SSH2_MSG_USERAUTH_REQUEST); | ||
| 454 | packet_put_cstring(authctxt->server_user); | ||
| 455 | packet_put_cstring(authctxt->service); | ||
| 456 | packet_put_cstring(authctxt->method->name); | ||
| 457 | packet_put_char(0); | ||
| 458 | packet_put_cstring(password); | ||
| 459 | packet_inject_ignore(64); | ||
| 460 | packet_send(); | ||
| 461 | return 1; | ||
| 462 | } | ||
| 463 | |||
| 464 | void | ||
| 465 | clear_auth_state(Authctxt *authctxt) | ||
| 466 | { | ||
| 467 | /* XXX clear authentication state */ | ||
| 468 | if (authctxt->last_key != NULL && authctxt->last_key_hint == -1) { | ||
| 469 | debug3("clear_auth_state: key_free %p", authctxt->last_key); | ||
| 470 | key_free(authctxt->last_key); | ||
| 471 | } | ||
| 472 | authctxt->last_key = NULL; | ||
| 473 | authctxt->last_key_hint = -2; | ||
| 474 | authctxt->last_key_sign = NULL; | ||
| 475 | } | ||
| 476 | |||
| 477 | int | ||
| 478 | sign_and_send_pubkey(Authctxt *authctxt, Key *k, sign_cb_fn *sign_callback) | ||
| 479 | { | ||
| 480 | Buffer b; | ||
| 481 | u_char *blob, *signature; | ||
| 482 | int bloblen, slen; | ||
| 483 | int skip = 0; | ||
| 484 | int ret = -1; | ||
| 485 | int have_sig = 1; | ||
| 486 | |||
| 487 | debug3("sign_and_send_pubkey"); | ||
| 488 | |||
| 489 | if (key_to_blob(k, &blob, &bloblen) == 0) { | ||
| 490 | /* we cannot handle this key */ | ||
| 491 | debug3("sign_and_send_pubkey: cannot handle key"); | ||
| 492 | return 0; | ||
| 493 | } | ||
| 494 | /* data to be signed */ | ||
| 495 | buffer_init(&b); | ||
| 496 | if (datafellows & SSH_OLD_SESSIONID) { | ||
| 497 | buffer_append(&b, session_id2, session_id2_len); | ||
| 498 | skip = session_id2_len; | ||
| 499 | } else { | ||
| 500 | buffer_put_string(&b, session_id2, session_id2_len); | ||
| 501 | skip = buffer_len(&b); | ||
| 502 | } | ||
| 503 | buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST); | ||
| 504 | buffer_put_cstring(&b, authctxt->server_user); | ||
| 505 | buffer_put_cstring(&b, | ||
| 506 | datafellows & SSH_BUG_PKSERVICE ? | ||
| 507 | "ssh-userauth" : | ||
| 508 | authctxt->service); | ||
| 509 | if (datafellows & SSH_BUG_PKAUTH) { | ||
| 510 | buffer_put_char(&b, have_sig); | ||
| 511 | } else { | ||
| 512 | buffer_put_cstring(&b, authctxt->method->name); | ||
| 513 | buffer_put_char(&b, have_sig); | ||
| 514 | buffer_put_cstring(&b, key_ssh_name(k)); | ||
| 515 | } | ||
| 516 | buffer_put_string(&b, blob, bloblen); | ||
| 517 | |||
| 518 | /* generate signature */ | ||
| 519 | ret = (*sign_callback)(authctxt, k, &signature, &slen, | ||
| 520 | buffer_ptr(&b), buffer_len(&b)); | ||
| 521 | if (ret == -1) { | ||
| 522 | xfree(blob); | ||
| 523 | buffer_free(&b); | ||
| 524 | return 0; | ||
| 525 | } | ||
| 526 | #ifdef DEBUG_PK | ||
| 527 | buffer_dump(&b); | ||
| 528 | #endif | ||
| 529 | if (datafellows & SSH_BUG_PKSERVICE) { | ||
| 530 | buffer_clear(&b); | ||
| 531 | buffer_append(&b, session_id2, session_id2_len); | ||
| 532 | skip = session_id2_len; | ||
| 533 | buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST); | ||
| 534 | buffer_put_cstring(&b, authctxt->server_user); | ||
| 535 | buffer_put_cstring(&b, authctxt->service); | ||
| 536 | buffer_put_cstring(&b, authctxt->method->name); | ||
| 537 | buffer_put_char(&b, have_sig); | ||
| 538 | if (!(datafellows & SSH_BUG_PKAUTH)) | ||
| 539 | buffer_put_cstring(&b, key_ssh_name(k)); | ||
| 540 | buffer_put_string(&b, blob, bloblen); | ||
| 541 | } | ||
| 542 | xfree(blob); | ||
| 543 | |||
| 544 | /* append signature */ | ||
| 545 | buffer_put_string(&b, signature, slen); | ||
| 546 | xfree(signature); | ||
| 547 | |||
| 548 | /* skip session id and packet type */ | ||
| 549 | if (buffer_len(&b) < skip + 1) | ||
| 550 | fatal("userauth_pubkey: internal error"); | ||
| 551 | buffer_consume(&b, skip + 1); | ||
| 552 | |||
| 553 | /* put remaining data from buffer into packet */ | ||
| 554 | packet_start(SSH2_MSG_USERAUTH_REQUEST); | ||
| 555 | packet_put_raw(buffer_ptr(&b), buffer_len(&b)); | ||
| 556 | buffer_free(&b); | ||
| 557 | packet_send(); | ||
| 558 | |||
| 559 | return 1; | ||
| 560 | } | ||
| 561 | |||
| 562 | int | ||
| 563 | send_pubkey_test(Authctxt *authctxt, Key *k, sign_cb_fn *sign_callback, | ||
| 564 | int hint) | ||
| 565 | { | ||
| 566 | u_char *blob; | ||
| 567 | int bloblen, have_sig = 0; | ||
| 568 | |||
| 569 | debug3("send_pubkey_test"); | ||
| 570 | |||
| 571 | if (key_to_blob(k, &blob, &bloblen) == 0) { | ||
| 572 | /* we cannot handle this key */ | ||
| 573 | debug3("send_pubkey_test: cannot handle key"); | ||
| 574 | return 0; | ||
| 575 | } | ||
| 576 | /* register callback for USERAUTH_PK_OK message */ | ||
| 577 | authctxt->last_key_sign = sign_callback; | ||
| 578 | authctxt->last_key_hint = hint; | ||
| 579 | authctxt->last_key = k; | ||
| 580 | dispatch_set(SSH2_MSG_USERAUTH_PK_OK, &input_userauth_pk_ok); | ||
| 581 | |||
| 582 | packet_start(SSH2_MSG_USERAUTH_REQUEST); | ||
| 583 | packet_put_cstring(authctxt->server_user); | ||
| 584 | packet_put_cstring(authctxt->service); | ||
| 585 | packet_put_cstring(authctxt->method->name); | ||
| 586 | packet_put_char(have_sig); | ||
| 587 | if (!(datafellows & SSH_BUG_PKAUTH)) | ||
| 588 | packet_put_cstring(key_ssh_name(k)); | ||
| 589 | packet_put_string(blob, bloblen); | ||
| 590 | xfree(blob); | ||
| 591 | packet_send(); | ||
| 592 | return 1; | ||
| 593 | } | ||
| 594 | |||
| 595 | Key * | ||
| 596 | load_identity_file(char *filename) | ||
| 597 | { | ||
| 598 | Key *private; | ||
| 599 | char prompt[300], *passphrase; | ||
| 600 | int quit, i; | ||
| 601 | struct stat st; | ||
| 602 | |||
| 603 | if (stat(filename, &st) < 0) { | ||
| 604 | debug3("no such identity: %s", filename); | ||
| 605 | return NULL; | ||
| 606 | } | ||
| 607 | private = key_load_private_type(KEY_UNSPEC, filename, "", NULL); | ||
| 608 | if (private == NULL) { | ||
| 609 | if (options.batch_mode) | ||
| 610 | return NULL; | ||
| 611 | snprintf(prompt, sizeof prompt, | ||
| 612 | "Enter passphrase for key '%.100s': ", filename); | ||
| 613 | for (i = 0; i < options.number_of_password_prompts; i++) { | ||
| 614 | passphrase = read_passphrase(prompt, 0); | ||
| 615 | if (strcmp(passphrase, "") != 0) { | ||
| 616 | private = key_load_private_type(KEY_UNSPEC, filename, | ||
| 617 | passphrase, NULL); | ||
| 618 | quit = 0; | ||
| 619 | } else { | ||
| 620 | debug2("no passphrase given, try next key"); | ||
| 621 | quit = 1; | ||
| 622 | } | ||
| 623 | memset(passphrase, 0, strlen(passphrase)); | ||
| 624 | xfree(passphrase); | ||
| 625 | if (private != NULL || quit) | ||
| 626 | break; | ||
| 627 | debug2("bad passphrase given, try again..."); | ||
| 628 | } | ||
| 629 | } | ||
| 630 | return private; | ||
| 631 | } | ||
| 632 | |||
| 633 | int | ||
| 634 | identity_sign_cb(Authctxt *authctxt, Key *key, u_char **sigp, int *lenp, | ||
| 635 | u_char *data, int datalen) | ||
| 636 | { | ||
| 637 | Key *private; | ||
| 638 | int idx, ret; | ||
| 639 | |||
| 640 | idx = authctxt->last_key_hint; | ||
| 641 | if (idx < 0) | ||
| 642 | return -1; | ||
| 643 | private = load_identity_file(options.identity_files[idx]); | ||
| 644 | if (private == NULL) | ||
| 645 | return -1; | ||
| 646 | ret = key_sign(private, sigp, lenp, data, datalen); | ||
| 647 | key_free(private); | ||
| 648 | return ret; | ||
| 649 | } | ||
| 650 | |||
| 651 | int agent_sign_cb(Authctxt *authctxt, Key *key, u_char **sigp, int *lenp, | ||
| 652 | u_char *data, int datalen) | ||
| 653 | { | ||
| 654 | return ssh_agent_sign(authctxt->agent, key, sigp, lenp, data, datalen); | ||
| 655 | } | ||
| 656 | |||
| 657 | int key_sign_cb(Authctxt *authctxt, Key *key, u_char **sigp, int *lenp, | ||
| 658 | u_char *data, int datalen) | ||
| 659 | { | ||
| 660 | return key_sign(key, sigp, lenp, data, datalen); | ||
| 661 | } | ||
| 662 | |||
| 663 | int | ||
| 664 | userauth_pubkey_agent(Authctxt *authctxt) | ||
| 665 | { | ||
| 666 | static int called = 0; | ||
| 667 | int ret = 0; | ||
| 668 | char *comment; | ||
| 669 | Key *k; | ||
| 670 | |||
| 671 | if (called == 0) { | ||
| 672 | if (ssh_get_num_identities(authctxt->agent, 2) == 0) | ||
| 673 | debug2("userauth_pubkey_agent: no keys at all"); | ||
| 674 | called = 1; | ||
| 675 | } | ||
| 676 | k = ssh_get_next_identity(authctxt->agent, &comment, 2); | ||
| 677 | if (k == NULL) { | ||
| 678 | debug2("userauth_pubkey_agent: no more keys"); | ||
| 679 | } else { | ||
| 680 | debug("userauth_pubkey_agent: testing agent key %s", comment); | ||
| 681 | xfree(comment); | ||
| 682 | ret = send_pubkey_test(authctxt, k, agent_sign_cb, -1); | ||
| 683 | if (ret == 0) | ||
| 684 | key_free(k); | ||
| 685 | } | ||
| 686 | if (ret == 0) | ||
| 687 | debug2("userauth_pubkey_agent: no message sent"); | ||
| 688 | return ret; | ||
| 689 | } | ||
| 690 | |||
| 691 | int | ||
| 692 | userauth_pubkey(Authctxt *authctxt) | ||
| 693 | { | ||
| 694 | static int idx = 0; | ||
| 695 | int sent = 0; | ||
| 696 | Key *key; | ||
| 697 | char *filename; | ||
| 698 | |||
| 699 | if (authctxt->agent != NULL) { | ||
| 700 | do { | ||
| 701 | sent = userauth_pubkey_agent(authctxt); | ||
| 702 | } while(!sent && authctxt->agent->howmany > 0); | ||
| 703 | } | ||
| 704 | while (!sent && idx < options.num_identity_files) { | ||
| 705 | key = options.identity_keys[idx]; | ||
| 706 | filename = options.identity_files[idx]; | ||
| 707 | if (key == NULL) { | ||
| 708 | debug("try privkey: %s", filename); | ||
| 709 | key = load_identity_file(filename); | ||
| 710 | if (key != NULL) { | ||
| 711 | sent = sign_and_send_pubkey(authctxt, key, | ||
| 712 | key_sign_cb); | ||
| 713 | key_free(key); | ||
| 714 | } | ||
| 715 | } else if (key->type != KEY_RSA1) { | ||
| 716 | debug("try pubkey: %s", filename); | ||
| 717 | sent = send_pubkey_test(authctxt, key, | ||
| 718 | identity_sign_cb, idx); | ||
| 719 | } | ||
| 720 | idx++; | ||
| 721 | } | ||
| 722 | return sent; | ||
| 723 | } | ||
| 724 | |||
| 725 | /* | ||
| 726 | * Send userauth request message specifying keyboard-interactive method. | ||
| 727 | */ | ||
| 728 | int | ||
| 729 | userauth_kbdint(Authctxt *authctxt) | ||
| 730 | { | ||
| 731 | static int attempt = 0; | ||
| 732 | |||
| 733 | if (attempt++ >= options.number_of_password_prompts) | ||
| 734 | return 0; | ||
| 735 | |||
| 736 | debug2("userauth_kbdint"); | ||
| 737 | packet_start(SSH2_MSG_USERAUTH_REQUEST); | ||
| 738 | packet_put_cstring(authctxt->server_user); | ||
| 739 | packet_put_cstring(authctxt->service); | ||
| 740 | packet_put_cstring(authctxt->method->name); | ||
| 741 | packet_put_cstring(""); /* lang */ | ||
| 742 | packet_put_cstring(options.kbd_interactive_devices ? | ||
| 743 | options.kbd_interactive_devices : ""); | ||
| 744 | packet_send(); | ||
| 745 | |||
| 746 | dispatch_set(SSH2_MSG_USERAUTH_INFO_REQUEST, &input_userauth_info_req); | ||
| 747 | return 1; | ||
| 748 | } | ||
| 749 | |||
| 750 | /* | ||
| 751 | * parse INFO_REQUEST, prompt user and send INFO_RESPONSE | ||
| 752 | */ | ||
| 753 | void | ||
| 754 | input_userauth_info_req(int type, int plen, void *ctxt) | ||
| 755 | { | ||
| 756 | Authctxt *authctxt = ctxt; | ||
| 757 | char *name, *inst, *lang, *prompt, *response; | ||
| 758 | u_int num_prompts, i; | ||
| 759 | int echo = 0; | ||
| 760 | |||
| 761 | debug2("input_userauth_info_req"); | ||
| 762 | |||
| 763 | if (authctxt == NULL) | ||
| 764 | fatal("input_userauth_info_req: no authentication context"); | ||
| 765 | |||
| 766 | name = packet_get_string(NULL); | ||
| 767 | inst = packet_get_string(NULL); | ||
| 768 | lang = packet_get_string(NULL); | ||
| 769 | if (strlen(name) > 0) | ||
| 770 | cli_mesg(name); | ||
| 771 | if (strlen(inst) > 0) | ||
| 772 | cli_mesg(inst); | ||
| 773 | xfree(name); | ||
| 774 | xfree(inst); | ||
| 775 | xfree(lang); | ||
| 776 | |||
| 777 | num_prompts = packet_get_int(); | ||
| 778 | /* | ||
| 779 | * Begin to build info response packet based on prompts requested. | ||
| 780 | * We commit to providing the correct number of responses, so if | ||
| 781 | * further on we run into a problem that prevents this, we have to | ||
| 782 | * be sure and clean this up and send a correct error response. | ||
| 783 | */ | ||
| 784 | packet_start(SSH2_MSG_USERAUTH_INFO_RESPONSE); | ||
| 785 | packet_put_int(num_prompts); | ||
| 786 | |||
| 787 | for (i = 0; i < num_prompts; i++) { | ||
| 788 | prompt = packet_get_string(NULL); | ||
| 789 | echo = packet_get_char(); | ||
| 790 | |||
| 791 | response = cli_prompt(prompt, echo); | ||
| 792 | |||
| 793 | packet_put_cstring(response); | ||
| 794 | memset(response, 0, strlen(response)); | ||
| 795 | xfree(response); | ||
| 796 | xfree(prompt); | ||
| 797 | } | ||
| 798 | packet_done(); /* done with parsing incoming message. */ | ||
| 799 | |||
| 800 | packet_inject_ignore(64); | ||
| 801 | packet_send(); | ||
| 802 | } | ||
| 803 | |||
| 804 | /* | ||
| 805 | * this will be move to an external program (ssh-keysign) ASAP. ssh-keysign | ||
| 806 | * will be setuid-root and the sbit can be removed from /usr/bin/ssh. | ||
| 807 | */ | ||
| 808 | int | ||
| 809 | userauth_hostbased(Authctxt *authctxt) | ||
| 810 | { | ||
| 811 | Key *private = NULL; | ||
| 812 | Buffer b; | ||
| 813 | u_char *signature, *blob; | ||
| 814 | char *chost, *pkalg, *p; | ||
| 815 | const char *service; | ||
| 816 | u_int blen, slen; | ||
| 817 | int ok, i, len, found = 0; | ||
| 818 | |||
| 819 | p = get_local_name(packet_get_connection_in()); | ||
| 820 | if (p == NULL) { | ||
| 821 | error("userauth_hostbased: cannot get local ipaddr/name"); | ||
| 822 | return 0; | ||
| 823 | } | ||
| 824 | len = strlen(p) + 2; | ||
| 825 | chost = xmalloc(len); | ||
| 826 | strlcpy(chost, p, len); | ||
| 827 | strlcat(chost, ".", len); | ||
| 828 | debug2("userauth_hostbased: chost %s", chost); | ||
| 829 | /* check for a useful key */ | ||
| 830 | for (i = 0; i < authctxt->nkeys; i++) { | ||
| 831 | private = authctxt->keys[i]; | ||
| 832 | if (private && private->type != KEY_RSA1) { | ||
| 833 | found = 1; | ||
| 834 | /* we take and free the key */ | ||
| 835 | authctxt->keys[i] = NULL; | ||
| 836 | break; | ||
| 837 | } | ||
| 838 | } | ||
| 839 | if (!found) { | ||
| 840 | xfree(chost); | ||
| 841 | return 0; | ||
| 842 | } | ||
| 843 | if (key_to_blob(private, &blob, &blen) == 0) { | ||
| 844 | key_free(private); | ||
| 845 | xfree(chost); | ||
| 846 | return 0; | ||
| 847 | } | ||
| 848 | service = datafellows & SSH_BUG_HBSERVICE ? "ssh-userauth" : | ||
| 849 | authctxt->service; | ||
| 850 | pkalg = xstrdup(key_ssh_name(private)); | ||
| 851 | buffer_init(&b); | ||
| 852 | /* construct data */ | ||
| 853 | buffer_put_string(&b, session_id2, session_id2_len); | ||
| 854 | buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST); | ||
| 855 | buffer_put_cstring(&b, authctxt->server_user); | ||
| 856 | buffer_put_cstring(&b, service); | ||
| 857 | buffer_put_cstring(&b, authctxt->method->name); | ||
| 858 | buffer_put_cstring(&b, pkalg); | ||
| 859 | buffer_put_string(&b, blob, blen); | ||
| 860 | buffer_put_cstring(&b, chost); | ||
| 861 | buffer_put_cstring(&b, authctxt->local_user); | ||
| 862 | #ifdef DEBUG_PK | ||
| 863 | buffer_dump(&b); | ||
| 864 | #endif | ||
| 865 | debug2("xxx: chost %s", chost); | ||
| 866 | ok = key_sign(private, &signature, &slen, buffer_ptr(&b), buffer_len(&b)); | ||
| 867 | key_free(private); | ||
| 868 | buffer_free(&b); | ||
| 869 | if (ok != 0) { | ||
| 870 | error("key_sign failed"); | ||
| 871 | xfree(chost); | ||
| 872 | xfree(pkalg); | ||
| 873 | return 0; | ||
| 874 | } | ||
| 875 | packet_start(SSH2_MSG_USERAUTH_REQUEST); | ||
| 876 | packet_put_cstring(authctxt->server_user); | ||
| 877 | packet_put_cstring(authctxt->service); | ||
| 878 | packet_put_cstring(authctxt->method->name); | ||
| 879 | packet_put_cstring(pkalg); | ||
| 880 | packet_put_string(blob, blen); | ||
| 881 | packet_put_cstring(chost); | ||
| 882 | packet_put_cstring(authctxt->local_user); | ||
| 883 | packet_put_string(signature, slen); | ||
| 884 | memset(signature, 's', slen); | ||
| 885 | xfree(signature); | ||
| 886 | xfree(chost); | ||
| 887 | xfree(pkalg); | ||
| 888 | |||
| 889 | packet_send(); | ||
| 890 | return 1; | ||
| 891 | } | ||
| 892 | |||
| 893 | /* find auth method */ | ||
| 894 | |||
| 895 | /* | ||
| 896 | * given auth method name, if configurable options permit this method fill | ||
| 897 | * in auth_ident field and return true, otherwise return false. | ||
| 898 | */ | ||
| 899 | int | ||
| 900 | authmethod_is_enabled(Authmethod *method) | ||
| 901 | { | ||
| 902 | if (method == NULL) | ||
| 903 | return 0; | ||
| 904 | /* return false if options indicate this method is disabled */ | ||
| 905 | if (method->enabled == NULL || *method->enabled == 0) | ||
| 906 | return 0; | ||
| 907 | /* return false if batch mode is enabled but method needs interactive mode */ | ||
| 908 | if (method->batch_flag != NULL && *method->batch_flag != 0) | ||
| 909 | return 0; | ||
| 910 | return 1; | ||
| 911 | } | ||
| 912 | |||
| 913 | Authmethod * | ||
| 914 | authmethod_lookup(const char *name) | ||
| 915 | { | ||
| 916 | Authmethod *method = NULL; | ||
| 917 | if (name != NULL) | ||
| 918 | for (method = authmethods; method->name != NULL; method++) | ||
| 919 | if (strcmp(name, method->name) == 0) | ||
| 920 | return method; | ||
| 921 | debug2("Unrecognized authentication method name: %s", name ? name : "NULL"); | ||
| 922 | return NULL; | ||
| 923 | } | ||
| 924 | |||
| 925 | /* XXX internal state */ | ||
| 926 | static Authmethod *current = NULL; | ||
| 927 | static char *supported = NULL; | ||
| 928 | static char *preferred = NULL; | ||
| 929 | /* | ||
| 930 | * Given the authentication method list sent by the server, return the | ||
| 931 | * next method we should try. If the server initially sends a nil list, | ||
| 932 | * use a built-in default list. | ||
| 933 | */ | ||
| 934 | Authmethod * | ||
| 935 | authmethod_get(char *authlist) | ||
| 936 | { | ||
| 937 | |||
| 938 | char *name = NULL; | ||
| 939 | int next; | ||
| 940 | |||
| 941 | /* Use a suitable default if we're passed a nil list. */ | ||
| 942 | if (authlist == NULL || strlen(authlist) == 0) | ||
| 943 | authlist = options.preferred_authentications; | ||
| 944 | |||
| 945 | if (supported == NULL || strcmp(authlist, supported) != 0) { | ||
| 946 | debug3("start over, passed a different list %s", authlist); | ||
| 947 | if (supported != NULL) | ||
| 948 | xfree(supported); | ||
| 949 | supported = xstrdup(authlist); | ||
| 950 | preferred = options.preferred_authentications; | ||
| 951 | debug3("preferred %s", preferred); | ||
| 952 | current = NULL; | ||
| 953 | } else if (current != NULL && authmethod_is_enabled(current)) | ||
| 954 | return current; | ||
| 955 | |||
| 956 | for (;;) { | ||
| 957 | if ((name = match_list(preferred, supported, &next)) == NULL) { | ||
| 958 | debug("no more auth methods to try"); | ||
| 959 | current = NULL; | ||
| 960 | return NULL; | ||
| 961 | } | ||
| 962 | preferred += next; | ||
| 963 | debug3("authmethod_lookup %s", name); | ||
| 964 | debug3("remaining preferred: %s", preferred); | ||
| 965 | if ((current = authmethod_lookup(name)) != NULL && | ||
| 966 | authmethod_is_enabled(current)) { | ||
| 967 | debug3("authmethod_is_enabled %s", name); | ||
| 968 | debug("next auth method to try is %s", name); | ||
| 969 | return current; | ||
| 970 | } | ||
| 971 | } | ||
| 972 | } | ||
| 973 | |||
| 974 | |||
| 975 | #define DELIM "," | ||
| 976 | char * | ||
| 977 | authmethods_get(void) | ||
| 978 | { | ||
| 979 | Authmethod *method = NULL; | ||
| 980 | char buf[1024]; | ||
| 981 | |||
| 982 | buf[0] = '\0'; | ||
| 983 | for (method = authmethods; method->name != NULL; method++) { | ||
| 984 | if (authmethod_is_enabled(method)) { | ||
| 985 | if (buf[0] != '\0') | ||
| 986 | strlcat(buf, DELIM, sizeof buf); | ||
| 987 | strlcat(buf, method->name, sizeof buf); | ||
| 988 | } | ||
| 989 | } | ||
| 990 | return xstrdup(buf); | ||
| 991 | } | ||
