1*0Sstevel@tonic-gate /* 2*0Sstevel@tonic-gate * Copyright (c) 2001 Markus Friedl. All rights reserved. 3*0Sstevel@tonic-gate * Copyright (c) 2001 Per Allansson. All rights reserved. 4*0Sstevel@tonic-gate * 5*0Sstevel@tonic-gate * Redistribution and use in source and binary forms, with or without 6*0Sstevel@tonic-gate * modification, are permitted provided that the following conditions 7*0Sstevel@tonic-gate * are met: 8*0Sstevel@tonic-gate * 1. Redistributions of source code must retain the above copyright 9*0Sstevel@tonic-gate * notice, this list of conditions and the following disclaimer. 10*0Sstevel@tonic-gate * 2. Redistributions in binary form must reproduce the above copyright 11*0Sstevel@tonic-gate * notice, this list of conditions and the following disclaimer in the 12*0Sstevel@tonic-gate * documentation and/or other materials provided with the distribution. 13*0Sstevel@tonic-gate * 14*0Sstevel@tonic-gate * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15*0Sstevel@tonic-gate * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16*0Sstevel@tonic-gate * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17*0Sstevel@tonic-gate * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18*0Sstevel@tonic-gate * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19*0Sstevel@tonic-gate * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20*0Sstevel@tonic-gate * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21*0Sstevel@tonic-gate * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22*0Sstevel@tonic-gate * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23*0Sstevel@tonic-gate * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24*0Sstevel@tonic-gate */ 25*0Sstevel@tonic-gate /* 26*0Sstevel@tonic-gate * Copyright 2003 Sun Microsystems, Inc. All rights reserved. 27*0Sstevel@tonic-gate * Use is subject to license terms. 28*0Sstevel@tonic-gate */ 29*0Sstevel@tonic-gate 30*0Sstevel@tonic-gate #include "includes.h" 31*0Sstevel@tonic-gate RCSID("$OpenBSD: auth2-chall.c,v 1.20 2002/06/30 21:59:45 deraadt Exp $"); 32*0Sstevel@tonic-gate 33*0Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 34*0Sstevel@tonic-gate 35*0Sstevel@tonic-gate #include "ssh2.h" 36*0Sstevel@tonic-gate #include "auth.h" 37*0Sstevel@tonic-gate #include "buffer.h" 38*0Sstevel@tonic-gate #include "packet.h" 39*0Sstevel@tonic-gate #include "xmalloc.h" 40*0Sstevel@tonic-gate #include "dispatch.h" 41*0Sstevel@tonic-gate #include "auth.h" 42*0Sstevel@tonic-gate #include "log.h" 43*0Sstevel@tonic-gate 44*0Sstevel@tonic-gate #ifndef lint 45*0Sstevel@tonic-gate static void auth2_challenge_start(Authctxt *); 46*0Sstevel@tonic-gate static int send_userauth_info_request(Authctxt *); 47*0Sstevel@tonic-gate static void input_userauth_info_response(int, u_int32_t, void *); 48*0Sstevel@tonic-gate 49*0Sstevel@tonic-gate #ifdef BSD_AUTH 50*0Sstevel@tonic-gate extern KbdintDevice bsdauth_device; 51*0Sstevel@tonic-gate #else 52*0Sstevel@tonic-gate #ifdef SKEY 53*0Sstevel@tonic-gate extern KbdintDevice skey_device; 54*0Sstevel@tonic-gate #endif 55*0Sstevel@tonic-gate #endif 56*0Sstevel@tonic-gate 57*0Sstevel@tonic-gate KbdintDevice *devices[] = { 58*0Sstevel@tonic-gate #ifdef BSD_AUTH 59*0Sstevel@tonic-gate &bsdauth_device, 60*0Sstevel@tonic-gate #else 61*0Sstevel@tonic-gate #ifdef SKEY 62*0Sstevel@tonic-gate &skey_device, 63*0Sstevel@tonic-gate #endif 64*0Sstevel@tonic-gate #endif 65*0Sstevel@tonic-gate NULL 66*0Sstevel@tonic-gate }; 67*0Sstevel@tonic-gate 68*0Sstevel@tonic-gate typedef struct KbdintAuthctxt KbdintAuthctxt; 69*0Sstevel@tonic-gate struct KbdintAuthctxt 70*0Sstevel@tonic-gate { 71*0Sstevel@tonic-gate char *devices; 72*0Sstevel@tonic-gate void *ctxt; 73*0Sstevel@tonic-gate KbdintDevice *device; 74*0Sstevel@tonic-gate u_int nreq; 75*0Sstevel@tonic-gate }; 76*0Sstevel@tonic-gate 77*0Sstevel@tonic-gate static KbdintAuthctxt * 78*0Sstevel@tonic-gate kbdint_alloc(const char *devs) 79*0Sstevel@tonic-gate { 80*0Sstevel@tonic-gate KbdintAuthctxt *kbdintctxt; 81*0Sstevel@tonic-gate Buffer b; 82*0Sstevel@tonic-gate int i; 83*0Sstevel@tonic-gate 84*0Sstevel@tonic-gate kbdintctxt = xmalloc(sizeof(KbdintAuthctxt)); 85*0Sstevel@tonic-gate if (strcmp(devs, "") == 0) { 86*0Sstevel@tonic-gate buffer_init(&b); 87*0Sstevel@tonic-gate for (i = 0; devices[i]; i++) { 88*0Sstevel@tonic-gate if (buffer_len(&b) > 0) 89*0Sstevel@tonic-gate buffer_append(&b, ",", 1); 90*0Sstevel@tonic-gate buffer_append(&b, devices[i]->name, 91*0Sstevel@tonic-gate strlen(devices[i]->name)); 92*0Sstevel@tonic-gate } 93*0Sstevel@tonic-gate buffer_append(&b, "\0", 1); 94*0Sstevel@tonic-gate kbdintctxt->devices = xstrdup(buffer_ptr(&b)); 95*0Sstevel@tonic-gate buffer_free(&b); 96*0Sstevel@tonic-gate } else { 97*0Sstevel@tonic-gate kbdintctxt->devices = xstrdup(devs); 98*0Sstevel@tonic-gate } 99*0Sstevel@tonic-gate debug("kbdint_alloc: devices '%s'", kbdintctxt->devices); 100*0Sstevel@tonic-gate kbdintctxt->ctxt = NULL; 101*0Sstevel@tonic-gate kbdintctxt->device = NULL; 102*0Sstevel@tonic-gate kbdintctxt->nreq = 0; 103*0Sstevel@tonic-gate 104*0Sstevel@tonic-gate return kbdintctxt; 105*0Sstevel@tonic-gate } 106*0Sstevel@tonic-gate static void 107*0Sstevel@tonic-gate kbdint_reset_device(KbdintAuthctxt *kbdintctxt) 108*0Sstevel@tonic-gate { 109*0Sstevel@tonic-gate if (kbdintctxt->ctxt) { 110*0Sstevel@tonic-gate kbdintctxt->device->free_ctx(kbdintctxt->ctxt); 111*0Sstevel@tonic-gate kbdintctxt->ctxt = NULL; 112*0Sstevel@tonic-gate } 113*0Sstevel@tonic-gate kbdintctxt->device = NULL; 114*0Sstevel@tonic-gate } 115*0Sstevel@tonic-gate static void 116*0Sstevel@tonic-gate kbdint_free(KbdintAuthctxt *kbdintctxt) 117*0Sstevel@tonic-gate { 118*0Sstevel@tonic-gate if (kbdintctxt->device) 119*0Sstevel@tonic-gate kbdint_reset_device(kbdintctxt); 120*0Sstevel@tonic-gate if (kbdintctxt->devices) { 121*0Sstevel@tonic-gate xfree(kbdintctxt->devices); 122*0Sstevel@tonic-gate kbdintctxt->devices = NULL; 123*0Sstevel@tonic-gate } 124*0Sstevel@tonic-gate xfree(kbdintctxt); 125*0Sstevel@tonic-gate } 126*0Sstevel@tonic-gate /* get next device */ 127*0Sstevel@tonic-gate static int 128*0Sstevel@tonic-gate kbdint_next_device(KbdintAuthctxt *kbdintctxt) 129*0Sstevel@tonic-gate { 130*0Sstevel@tonic-gate size_t len; 131*0Sstevel@tonic-gate char *t; 132*0Sstevel@tonic-gate int i; 133*0Sstevel@tonic-gate 134*0Sstevel@tonic-gate if (kbdintctxt->device) 135*0Sstevel@tonic-gate kbdint_reset_device(kbdintctxt); 136*0Sstevel@tonic-gate do { 137*0Sstevel@tonic-gate len = kbdintctxt->devices ? 138*0Sstevel@tonic-gate strcspn(kbdintctxt->devices, ",") : 0; 139*0Sstevel@tonic-gate 140*0Sstevel@tonic-gate if (len == 0) 141*0Sstevel@tonic-gate break; 142*0Sstevel@tonic-gate for (i = 0; devices[i]; i++) 143*0Sstevel@tonic-gate if (strncmp(kbdintctxt->devices, devices[i]->name, len) == 0) 144*0Sstevel@tonic-gate kbdintctxt->device = devices[i]; 145*0Sstevel@tonic-gate t = kbdintctxt->devices; 146*0Sstevel@tonic-gate kbdintctxt->devices = t[len] ? xstrdup(t+len+1) : NULL; 147*0Sstevel@tonic-gate xfree(t); 148*0Sstevel@tonic-gate debug2("kbdint_next_device: devices %s", kbdintctxt->devices ? 149*0Sstevel@tonic-gate kbdintctxt->devices : "<empty>"); 150*0Sstevel@tonic-gate } while (kbdintctxt->devices && !kbdintctxt->device); 151*0Sstevel@tonic-gate 152*0Sstevel@tonic-gate return kbdintctxt->device ? 1 : 0; 153*0Sstevel@tonic-gate } 154*0Sstevel@tonic-gate 155*0Sstevel@tonic-gate /* 156*0Sstevel@tonic-gate * try challenge-response, set authctxt->method->postponed if we have to 157*0Sstevel@tonic-gate * wait for the response. 158*0Sstevel@tonic-gate */ 159*0Sstevel@tonic-gate void 160*0Sstevel@tonic-gate auth2_challenge(Authctxt *authctxt, char *devs) 161*0Sstevel@tonic-gate { 162*0Sstevel@tonic-gate debug("auth2_challenge: user=%s devs=%s", 163*0Sstevel@tonic-gate authctxt->user ? authctxt->user : "<nouser>", 164*0Sstevel@tonic-gate devs ? devs : "<no devs>"); 165*0Sstevel@tonic-gate 166*0Sstevel@tonic-gate if (authctxt->user == NULL || !devs) 167*0Sstevel@tonic-gate return; 168*0Sstevel@tonic-gate if (authctxt->method->method_data != NULL) { 169*0Sstevel@tonic-gate auth2_challenge_abandon(authctxt); 170*0Sstevel@tonic-gate authctxt->method->abandoned = 0; 171*0Sstevel@tonic-gate } 172*0Sstevel@tonic-gate authctxt->method->method_data = (void *) kbdint_alloc(devs); 173*0Sstevel@tonic-gate auth2_challenge_start(authctxt); 174*0Sstevel@tonic-gate } 175*0Sstevel@tonic-gate 176*0Sstevel@tonic-gate /* unregister kbd-int callbacks and context */ 177*0Sstevel@tonic-gate static void 178*0Sstevel@tonic-gate auth2_challenge_stop(Authctxt *authctxt) 179*0Sstevel@tonic-gate { 180*0Sstevel@tonic-gate /* unregister callback */ 181*0Sstevel@tonic-gate dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE, NULL); 182*0Sstevel@tonic-gate if (authctxt->method->method_data != NULL) { 183*0Sstevel@tonic-gate kbdint_free((KbdintAuthctxt *) authctxt->method->method_data); 184*0Sstevel@tonic-gate authctxt->method->method_data = NULL; 185*0Sstevel@tonic-gate } 186*0Sstevel@tonic-gate } 187*0Sstevel@tonic-gate 188*0Sstevel@tonic-gate void 189*0Sstevel@tonic-gate auth2_challenge_abandon(Authctxt *authctxt) 190*0Sstevel@tonic-gate { 191*0Sstevel@tonic-gate auth2_challenge_stop(authctxt); 192*0Sstevel@tonic-gate authctxt->method->abandoned = 1; 193*0Sstevel@tonic-gate authctxt->method->postponed = 0; 194*0Sstevel@tonic-gate authctxt->method->authenticated = 0; 195*0Sstevel@tonic-gate authctxt->method->abandons++; 196*0Sstevel@tonic-gate authctxt->method->attempts++; 197*0Sstevel@tonic-gate } 198*0Sstevel@tonic-gate 199*0Sstevel@tonic-gate /* side effect: sets authctxt->method->postponed if a reply was sent*/ 200*0Sstevel@tonic-gate static void 201*0Sstevel@tonic-gate auth2_challenge_start(Authctxt *authctxt) 202*0Sstevel@tonic-gate { 203*0Sstevel@tonic-gate KbdintAuthctxt *kbdintctxt = (KbdintAuthctxt *) 204*0Sstevel@tonic-gate authctxt->method->method_data; 205*0Sstevel@tonic-gate 206*0Sstevel@tonic-gate debug2("auth2_challenge_start: devices %s", 207*0Sstevel@tonic-gate kbdintctxt->devices ? kbdintctxt->devices : "<empty>"); 208*0Sstevel@tonic-gate 209*0Sstevel@tonic-gate if (kbdint_next_device(kbdintctxt) == 0) { 210*0Sstevel@tonic-gate auth2_challenge_stop(authctxt); 211*0Sstevel@tonic-gate return; 212*0Sstevel@tonic-gate } 213*0Sstevel@tonic-gate debug("auth2_challenge_start: trying authentication method '%s'", 214*0Sstevel@tonic-gate kbdintctxt->device->name); 215*0Sstevel@tonic-gate 216*0Sstevel@tonic-gate if ((kbdintctxt->ctxt = kbdintctxt->device->init_ctx(authctxt)) == NULL) { 217*0Sstevel@tonic-gate auth2_challenge_stop(authctxt); 218*0Sstevel@tonic-gate return; 219*0Sstevel@tonic-gate } 220*0Sstevel@tonic-gate if (send_userauth_info_request(authctxt) == 0) { 221*0Sstevel@tonic-gate auth2_challenge_stop(authctxt); 222*0Sstevel@tonic-gate return; 223*0Sstevel@tonic-gate } 224*0Sstevel@tonic-gate dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE, 225*0Sstevel@tonic-gate &input_userauth_info_response); 226*0Sstevel@tonic-gate 227*0Sstevel@tonic-gate authctxt->method->postponed = 1; 228*0Sstevel@tonic-gate } 229*0Sstevel@tonic-gate 230*0Sstevel@tonic-gate static int 231*0Sstevel@tonic-gate send_userauth_info_request(Authctxt *authctxt) 232*0Sstevel@tonic-gate { 233*0Sstevel@tonic-gate KbdintAuthctxt *kbdintctxt; 234*0Sstevel@tonic-gate char *name, *instr, **prompts; 235*0Sstevel@tonic-gate int i; 236*0Sstevel@tonic-gate u_int *echo_on; 237*0Sstevel@tonic-gate 238*0Sstevel@tonic-gate kbdintctxt = (KbdintAuthctxt *) authctxt->method->method_data; 239*0Sstevel@tonic-gate if (kbdintctxt->device->query(kbdintctxt->ctxt, 240*0Sstevel@tonic-gate &name, &instr, &kbdintctxt->nreq, &prompts, &echo_on)) 241*0Sstevel@tonic-gate return 0; 242*0Sstevel@tonic-gate 243*0Sstevel@tonic-gate packet_start(SSH2_MSG_USERAUTH_INFO_REQUEST); 244*0Sstevel@tonic-gate packet_put_cstring(name); 245*0Sstevel@tonic-gate packet_put_cstring(instr); 246*0Sstevel@tonic-gate packet_put_cstring(""); /* language not used */ 247*0Sstevel@tonic-gate packet_put_int(kbdintctxt->nreq); 248*0Sstevel@tonic-gate for (i = 0; i < kbdintctxt->nreq; i++) { 249*0Sstevel@tonic-gate packet_put_cstring(prompts[i]); 250*0Sstevel@tonic-gate packet_put_char(echo_on[i]); 251*0Sstevel@tonic-gate } 252*0Sstevel@tonic-gate packet_send(); 253*0Sstevel@tonic-gate packet_write_wait(); 254*0Sstevel@tonic-gate 255*0Sstevel@tonic-gate for (i = 0; i < kbdintctxt->nreq; i++) 256*0Sstevel@tonic-gate xfree(prompts[i]); 257*0Sstevel@tonic-gate xfree(prompts); 258*0Sstevel@tonic-gate xfree(echo_on); 259*0Sstevel@tonic-gate xfree(name); 260*0Sstevel@tonic-gate xfree(instr); 261*0Sstevel@tonic-gate return 1; 262*0Sstevel@tonic-gate } 263*0Sstevel@tonic-gate 264*0Sstevel@tonic-gate static void 265*0Sstevel@tonic-gate input_userauth_info_response(int type, u_int32_t seq, void *ctxt) 266*0Sstevel@tonic-gate { 267*0Sstevel@tonic-gate Authctxt *authctxt = ctxt; 268*0Sstevel@tonic-gate KbdintAuthctxt *kbdintctxt; 269*0Sstevel@tonic-gate int i, res, len; 270*0Sstevel@tonic-gate u_int nresp; 271*0Sstevel@tonic-gate char **response = NULL, *method; 272*0Sstevel@tonic-gate 273*0Sstevel@tonic-gate if (authctxt == NULL) 274*0Sstevel@tonic-gate fatal("input_userauth_info_response: no authctxt"); 275*0Sstevel@tonic-gate kbdintctxt = (KbdintAuthctxt *) authctxt->method->method_data; 276*0Sstevel@tonic-gate if (kbdintctxt == NULL || kbdintctxt->ctxt == NULL) 277*0Sstevel@tonic-gate fatal("input_userauth_info_response: no kbdintctxt"); 278*0Sstevel@tonic-gate if (kbdintctxt->device == NULL) 279*0Sstevel@tonic-gate fatal("input_userauth_info_response: no device"); 280*0Sstevel@tonic-gate 281*0Sstevel@tonic-gate nresp = packet_get_int(); 282*0Sstevel@tonic-gate if (nresp != kbdintctxt->nreq) 283*0Sstevel@tonic-gate fatal("input_userauth_info_response: wrong number of replies"); 284*0Sstevel@tonic-gate if (nresp > 100) 285*0Sstevel@tonic-gate fatal("input_userauth_info_response: too many replies"); 286*0Sstevel@tonic-gate if (nresp > 0) { 287*0Sstevel@tonic-gate response = xmalloc(nresp * sizeof(char *)); 288*0Sstevel@tonic-gate for (i = 0; i < nresp; i++) 289*0Sstevel@tonic-gate response[i] = packet_get_string(NULL); 290*0Sstevel@tonic-gate } 291*0Sstevel@tonic-gate packet_check_eom(); 292*0Sstevel@tonic-gate 293*0Sstevel@tonic-gate if (authctxt->valid) { 294*0Sstevel@tonic-gate res = kbdintctxt->device->respond(kbdintctxt->ctxt, 295*0Sstevel@tonic-gate nresp, response); 296*0Sstevel@tonic-gate } else { 297*0Sstevel@tonic-gate res = -1; 298*0Sstevel@tonic-gate } 299*0Sstevel@tonic-gate 300*0Sstevel@tonic-gate for (i = 0; i < nresp; i++) { 301*0Sstevel@tonic-gate memset(response[i], 'r', strlen(response[i])); 302*0Sstevel@tonic-gate xfree(response[i]); 303*0Sstevel@tonic-gate } 304*0Sstevel@tonic-gate if (response) 305*0Sstevel@tonic-gate xfree(response); 306*0Sstevel@tonic-gate 307*0Sstevel@tonic-gate authctxt->method->postponed = 0; /* reset */ 308*0Sstevel@tonic-gate switch (res) { 309*0Sstevel@tonic-gate case 0: 310*0Sstevel@tonic-gate /* Success! */ 311*0Sstevel@tonic-gate authctxt->method->authenticated = 1; 312*0Sstevel@tonic-gate break; 313*0Sstevel@tonic-gate case 1: 314*0Sstevel@tonic-gate /* Authentication needs further interaction */ 315*0Sstevel@tonic-gate if (send_userauth_info_request(authctxt) == 1) { 316*0Sstevel@tonic-gate authctxt->method->postponed = 1; 317*0Sstevel@tonic-gate } 318*0Sstevel@tonic-gate break; 319*0Sstevel@tonic-gate default: 320*0Sstevel@tonic-gate /* Failure! */ 321*0Sstevel@tonic-gate break; 322*0Sstevel@tonic-gate } 323*0Sstevel@tonic-gate 324*0Sstevel@tonic-gate 325*0Sstevel@tonic-gate len = strlen("keyboard-interactive") + 2 + 326*0Sstevel@tonic-gate strlen(kbdintctxt->device->name); 327*0Sstevel@tonic-gate method = xmalloc(len); 328*0Sstevel@tonic-gate snprintf(method, len, "keyboard-interactive/%s", 329*0Sstevel@tonic-gate kbdintctxt->device->name); 330*0Sstevel@tonic-gate 331*0Sstevel@tonic-gate if (authctxt->method->authenticated || authctxt->method->abandoned) { 332*0Sstevel@tonic-gate auth2_challenge_stop(authctxt); 333*0Sstevel@tonic-gate } else { 334*0Sstevel@tonic-gate /* start next device */ 335*0Sstevel@tonic-gate /* may set authctxt->method->postponed */ 336*0Sstevel@tonic-gate auth2_challenge_start(authctxt); 337*0Sstevel@tonic-gate } 338*0Sstevel@tonic-gate userauth_finish(authctxt, method); 339*0Sstevel@tonic-gate xfree(method); 340*0Sstevel@tonic-gate } 341*0Sstevel@tonic-gate 342*0Sstevel@tonic-gate void 343*0Sstevel@tonic-gate privsep_challenge_enable(void) 344*0Sstevel@tonic-gate { 345*0Sstevel@tonic-gate #ifdef BSD_AUTH 346*0Sstevel@tonic-gate extern KbdintDevice mm_bsdauth_device; 347*0Sstevel@tonic-gate #endif 348*0Sstevel@tonic-gate #ifdef SKEY 349*0Sstevel@tonic-gate extern KbdintDevice mm_skey_device; 350*0Sstevel@tonic-gate #endif 351*0Sstevel@tonic-gate /* As long as SSHv1 has devices[0] hard coded this is fine */ 352*0Sstevel@tonic-gate #ifdef BSD_AUTH 353*0Sstevel@tonic-gate devices[0] = &mm_bsdauth_device; 354*0Sstevel@tonic-gate #else 355*0Sstevel@tonic-gate #ifdef SKEY 356*0Sstevel@tonic-gate devices[0] = &mm_skey_device; 357*0Sstevel@tonic-gate #endif 358*0Sstevel@tonic-gate #endif 359*0Sstevel@tonic-gate } 360*0Sstevel@tonic-gate #endif /* lint */ 361